diff --git a/eng/config.json b/eng/config.json index 070b0daf25b0..f5d6d44e6f15 100644 --- a/eng/config.json +++ b/eng/config.json @@ -42,7 +42,7 @@ }, { "Name": "azopenai", - "CoverageGoal": 0.39 + "CoverageGoal": 0.34 }, { "Name": "aztemplate", @@ -110,4 +110,4 @@ "CoverageGoal": 0.80 } ] -} \ No newline at end of file +} diff --git a/sdk/ai/azopenai/CHANGELOG.md b/sdk/ai/azopenai/CHANGELOG.md index af2ec816d5ee..dc2f97a3cead 100644 --- a/sdk/ai/azopenai/CHANGELOG.md +++ b/sdk/ai/azopenai/CHANGELOG.md @@ -1,10 +1,13 @@ # Release History -## 0.2.1 (Unreleased) +## 0.3.0 (Unreleased) ### Features Added +- Support for Whisper audio APIs for transcription and translation using `GetAudioTranscription` and `GetAudioTranslation`. ### Breaking Changes +- ChatChoiceContentFilterResults content filtering fields are now all typed as ContentFilterResult, instead of unique types for each field. +- `PromptAnnotations` renamed to `PromptFilterResults` in `ChatCompletions` and `Completions`. ### Bugs Fixed diff --git a/sdk/ai/azopenai/assets.json b/sdk/ai/azopenai/assets.json index 1d2632ea7def..bc1ac87892ae 100644 --- a/sdk/ai/azopenai/assets.json +++ b/sdk/ai/azopenai/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "go", "TagPrefix": "go/ai/azopenai", - "Tag": "go/ai/azopenai_7be6ae3c15" + "Tag": "go/ai/azopenai_5ce13f37c4" } diff --git a/sdk/ai/azopenai/autorest.md b/sdk/ai/azopenai/autorest.md index 664d9f0ed630..9ebcfbc58157 100644 --- a/sdk/ai/azopenai/autorest.md +++ b/sdk/ai/azopenai/autorest.md @@ -96,7 +96,7 @@ directive: transform: $["$ref"] = "#/components/schemas/State"; delete $.allOf; - from: openapi-document where: $.components.schemas["ContentFilterResult"].properties.severity - transform: $["$ref"] = "#/components/schemas/ContentFilterSeverity"; delete $.allOf; + transform: $.$ref = $.allOf[0].$ref; delete $.allOf; - from: openapi-document where: $.components.schemas["ChatChoice"].properties.finish_reason transform: $["$ref"] = "#/components/schemas/CompletionsFinishReason"; delete $.oneOf; @@ -109,6 +109,102 @@ directive: - from: openapi-document where: $.components.schemas["AzureCognitiveSearchChatExtensionConfiguration"].properties.queryType transform: $["$ref"] = "#/components/schemas/AzureCognitiveSearchQueryType"; delete $.allOf; + - from: openapi-document + where: $.components.schemas["ContentFilterResults"].properties.sexual + transform: $.$ref = $.allOf[0].$ref; delete $.allOf; + - from: openapi-document + where: $.components.schemas["ContentFilterResults"].properties.hate + transform: $.$ref = $.allOf[0].$ref; delete $.allOf; + - from: openapi-document + where: $.components.schemas["ContentFilterResults"].properties.self_harm + transform: $.$ref = $.allOf[0].$ref; delete $.allOf; + - from: openapi-document + where: $.components.schemas["ContentFilterResults"].properties.violence + transform: $.$ref = $.allOf[0].$ref; delete $.allOf; + + # + # [BEGIN] Whisper + # + + # the whisper operations are really long since they are a conglomeration of _all_ the + # possible return types. + - rename-operation: + from: getAudioTranscriptionAsPlainText_getAudioTranscriptionAsResponseObject + to: GetAudioTranscriptionInternal + - rename-operation: + from: getAudioTranslationAsPlainText_getAudioTranslationAsResponseObject + to: GetAudioTranslationInternal + + # fixup the responses + - from: openapi-document + where: $.paths["/deployments/{deploymentId}/audio/transcriptions"] + transform: | + delete $.post.responses["200"].statusCode; + $.post.responses["200"].content["application/json"].schema["$ref"] = "#/components/schemas/AudioTranscription"; delete $.post.responses["200"].content["application/json"].schema.anyOf; + - from: openapi-document + where: $.paths["/deployments/{deploymentId}/audio/translations"] + transform: | + delete $.post.responses["200"].statusCode; + $.post.responses["200"].content["application/json"].schema["$ref"] = "#/components/schemas/AudioTranscription"; delete $.post.responses["200"].content["application/json"].schema.anyOf; + + # hide the generated functions, in favor of our public wrappers. + - from: + - client.go + - models.go + - models_serde.go + - response_types.go + - options.go + where: $ + transform: | + return $ + .replace(/GetAudioTranscriptionInternal([^){ ]*)/g, "getAudioTranscriptionInternal$1") + .replace(/GetAudioTranslationInternal([^){ ]*)/g, "getAudioTranslationInternal$1"); + + # some multipart fixing + - from: client.go + where: $ + transform: | + return $ + .replace(/(func.*getAudio(?:Translation|Transcription)InternalCreateRequest\(.+?)options/g, "$1body") + .replace(/runtime\.SetMultipartFormData\(.+?\)/sg, "setMultipartFormData(req, file, *body)") + + # response type parsing (can be text/plain _or_ JSON) + - from: client.go + where: $ + transform: | + return $ + .replace(/client\.getAudioTranscriptionInternalHandleResponse/g, "getAudioTranscriptionInternalHandleResponse") + .replace(/client\.getAudioTranslationInternalHandleResponse/g, "getAudioTranslationInternalHandleResponse") + + # Whisper openapi3 generation: we have two oneOf that point to the same type. + # and we want to activate our multipart support in the generator. + - from: openapi-document + where: $.paths + transform: | + let makeMultipart = (item) => { + if (item["application/json"] == null) { return item; } + item["multipart/form-data"] = { + ...item["application/json"] + }; + delete item["application/json"]; + } + makeMultipart($["/deployments/{deploymentId}/audio/transcriptions"].post.requestBody.content); + makeMultipart($["/deployments/{deploymentId}/audio/translations"].post.requestBody.content); + + - from: openapi-document + where: $.components.schemas + transform: | + let fix = (v) => { if (v.allOf != null) { v.$ref = v.allOf[0].$ref; delete v.allOf; } }; + + fix($.AudioTranscriptionOptions.properties.response_format); + fix($.AudioTranscription.properties.task); + + fix($.AudioTranslationOptions.properties.response_format); + fix($.AudioTranslation.properties.task); + # + # [END] Whisper + # + # Fix "AutoGenerated" models - from: openapi-document where: $.components.schemas["ChatCompletions"].properties.usage @@ -155,13 +251,26 @@ directive: - models_serde.go - models.go where: $ - transform: return $.replace(/AzureCoreFoundations/g, "azureCoreFoundations"); - - from: - - models_serde.go - - models.go - where: $ - transform: return $.replace(/(?:\/\/.*\s)?func \(\w \*?(?:ErrorResponse|ErrorResponseError|InnerError|InnerErrorInnererror)\).*\{\s(?:.+\s)+\}\s/g, ""); - + transform: | + return $ + // InnerError is actually a recursive type, no need for this innererrorinnererror type + .replace(/\/\/ AzureCoreFoundationsInnerErrorInnererror.+?\n}/s, "") + // also, remove its marshalling functions + .replace(/\/\/ (Unmarshal|Marshal)JSON implements[^\n]+?AzureCoreFoundationsInnerErrorInnererror.+?\n}/sg, "") + + // Remove any references to the type and replace them with InnerError. + .replace(/Innererror \*(AzureCoreFoundationsInnerErrorInnererror|AzureCoreFoundationsErrorInnererror)/g, "InnerError *InnerError") + + // Fix the marshallers/unmarshallers to use the right case. + .replace(/(a|c).Innererror/g, '$1.InnerError') + + // We have two "inner error" types that are identical (ErrorInnerError and InnerError). Let's eliminate the one that's not actually directly referenced. + .replace(/\/\/azureCoreFoundationsInnerError.+?\n}/s, "") + + // + // Fix the AzureCoreFoundation naming to match our style. + // + .replace(/AzureCoreFoundations/g, "") - from: constants.go where: $ transform: >- @@ -185,15 +294,6 @@ directive: return $ .replace(/runtime\.JoinPaths\(client.endpoint, urlPath\)/g, "client.formatURL(urlPath, getDeployment(body))"); - # Some ImageGenerations hackery to represent the ImageLocation/ImagePayload polymorphism. - # - Remove the auto-generated ImageGenerationsDataItem. - # - Replace the ImageGenerations.Data type with []ImageGenerationDataItem - # - from: models.go - # where: $ - # transform: | - # return $.replace(/type ImageGenerationsDataItem struct {[^}]+}/, "// ImageGenerationsDataItem represents an image URL or payload\ntype ImageGenerationsDataItem struct{\nImageLocation\nImagePayload\n}") - # $.replace(/(type ImageGenerations struct.+?)Data any/g, "$1Data []ImageGenerationsDataItem") - - from: models.go where: $ transform: | @@ -261,16 +361,6 @@ directive: where: $ transform: return $.replace(/Logprobs/g, "LogProbs") - # delete ContentFilterResult in favor of our custom representation. - - from: - - models.go - - models_serde.go - where: $ - transform: | - return $.replace(/\/\/ ContentFilterResult.+?\n}/s, "") - .replace(/\/\/ MarshalJSON implements the json.Marshaller interface for type ContentFilterResult.+?\n}/s, "") - .replace(/\/\/ UnmarshalJSON implements the json.Unmarshaller interface for type ContentFilterResult.+?\n}/s, ""); - - from: constants.go where: $ transform: return $.replace(/\/\/ PossibleazureOpenAIOperationStateValues returns.+?\n}/s, ""); @@ -295,14 +385,14 @@ directive: where: $ transform: | return $ - .replace(/\/\/ The model name.*?Model \*string/sg, "// REQUIRED: Deployment specifies the name of the deployment (for Azure OpenAI) or model (for OpenAI) to use for this request.\nDeployment string"); + .replace(/\/\/ The model.*?Model \*string/sg, "// REQUIRED: Deployment specifies the name of the deployment (for Azure OpenAI) or model (for OpenAI) to use for this request.\nDeployment string"); - from: models_serde.go where: $ transform: | return $ - .replace(/populate\(objectMap, "model", (c|e).Model\)/g, 'populate(objectMap, "model", &$1.Deployment)') - .replace(/err = unpopulate\(val, "Model", &(c|e).Model\)/g, 'err = unpopulate(val, "Model", &$1.Deployment)'); + .replace(/populate\(objectMap, "model", (c|e|a).Model\)/g, 'populate(objectMap, "model", &$1.Deployment)') + .replace(/err = unpopulate\(val, "Model", &(c|e|a).Model\)/g, 'err = unpopulate(val, "Model", &$1.Deployment)'); # Make the Azure extensions internal - we expose these through the GetChatCompletions*() functions # and just treat which endpoint we use as an implementation detail. @@ -344,4 +434,9 @@ directive: return $.replace( /(AzureChatExtensionTypeAzureCognitiveSearch AzureChatExtensionType)/, "// AzureChatExtensionTypeAzureCognitiveSearch enables the use of an Azure Cognitive Search index with chat completions.\n// [AzureChatExtensionConfiguration.Parameter] should be of type [AzureCognitiveSearchChatExtensionConfiguration].\n$1"); + + # HACK: prompt_filter_results <-> prompt_annotations change + - from: models_serde.go + where: $ + transform: return $.replace(/case "prompt_filter_results":/g, 'case "prompt_annotations":\nfallthrough\ncase "prompt_filter_results":') ``` diff --git a/sdk/ai/azopenai/ci.yml b/sdk/ai/azopenai/ci.yml index 39e2390cb86d..29540abe1ee4 100644 --- a/sdk/ai/azopenai/ci.yml +++ b/sdk/ai/azopenai/ci.yml @@ -38,15 +38,15 @@ stages: # Azure OpenAI AOAI_ENDPOINT: $(AOAI-ENDPOINT) AOAI_API_KEY: $(AOAI-API-KEY) - AOAI_CHAT_COMPLETIONS_MODEL_DEPLOYMENT: $(AOAI-CHAT-COMPLETIONS-MODEL-DEPLOYMENT) - AOAI_COMPLETIONS_MODEL_DEPLOYMENT: $(AOAI-COMPLETIONS-MODEL-DEPLOYMENT) - AOAI_EMBEDDINGS_MODEL_DEPLOYMENT: $(AOAI-EMBEDDINGS-MODEL-DEPLOYMENT) + AOAI_CHAT_COMPLETIONS_MODEL: $(AOAI-CHAT-COMPLETIONS-MODEL-DEPLOYMENT) + AOAI_COMPLETIONS_MODEL: $(AOAI-COMPLETIONS-MODEL-DEPLOYMENT) + AOAI_EMBEDDINGS_MODEL: $(AOAI-EMBEDDINGS-MODEL-DEPLOYMENT) # Azure OpenAI "Canary" - AOAI_COMPLETIONS_MODEL_DEPLOYMENT_CANARY: $(AOAI-COMPLETIONS-MODEL-DEPLOYMENT-CANARY) + AOAI_COMPLETIONS_MODEL_CANARY: $(AOAI-COMPLETIONS-MODEL-DEPLOYMENT-CANARY) AOAI_API_KEY_CANARY: $(AOAI-API-KEY-CANARY) - AOAI_EMBEDDINGS_MODEL_DEPLOYMENT_CANARY: $(AOAI-EMBEDDINGS-MODEL-DEPLOYMENT-CANARY) - AOAI_CHAT_COMPLETIONS_MODEL_DEPLOYMENT_CANARY: $(AOAI-CHAT-COMPLETIONS-MODEL-DEPLOYMENT-CANARY) + AOAI_EMBEDDINGS_MODEL_CANARY: $(AOAI-EMBEDDINGS-MODEL-DEPLOYMENT-CANARY) + AOAI_CHAT_COMPLETIONS_MODEL_CANARY: $(AOAI-CHAT-COMPLETIONS-MODEL-DEPLOYMENT-CANARY) AOAI_ENDPOINT_CANARY: $(AOAI-ENDPOINT-CANARY) # OpenAI @@ -61,3 +61,6 @@ stages: COGNITIVE_SEARCH_API_INDEX: $(COGNITIVE-SEARCH-API-INDEX) COGNITIVE_SEARCH_API_KEY: $(COGNITIVE-SEARCH-API-KEY) + AOAI_ENDPOINT_WHISPER: $(AOAI-ENDPOINT-WHISPER) + AOAI_API_KEY_WHISPER: $(AOAI-API-KEY-WHISPER) + AOAI_MODEL_WHISPER: $(AOAI-MODEL-WHISPER) diff --git a/sdk/ai/azopenai/client.go b/sdk/ai/azopenai/client.go index 9ffe12f5e27f..59c064e9bbf2 100644 --- a/sdk/ai/azopenai/client.go +++ b/sdk/ai/azopenai/client.go @@ -27,7 +27,7 @@ type Client struct { // beginAzureBatchImageGeneration - Starts the generation of a batch of images from a text caption // If the operation fails it returns an *azcore.ResponseError type. // -// Generated from API version 2023-08-01-preview +// Generated from API version 2023-09-01-preview // - options - beginAzureBatchImageGenerationOptions contains the optional parameters for the Client.beginAzureBatchImageGeneration // method. func (client *Client) beginAzureBatchImageGeneration(ctx context.Context, body ImageGenerationOptions, options *beginAzureBatchImageGenerationOptions) (*runtime.Poller[azureBatchImageGenerationInternalResponse], error) { @@ -46,7 +46,7 @@ func (client *Client) beginAzureBatchImageGeneration(ctx context.Context, body I // AzureBatchImageGenerationInternal - Starts the generation of a batch of images from a text caption // If the operation fails it returns an *azcore.ResponseError type. // -// Generated from API version 2023-08-01-preview +// Generated from API version 2023-09-01-preview func (client *Client) azureBatchImageGenerationInternal(ctx context.Context, body ImageGenerationOptions, options *beginAzureBatchImageGenerationOptions) (*http.Response, error) { var err error req, err := client.azureBatchImageGenerationInternalCreateRequest(ctx, body, options) @@ -72,7 +72,7 @@ func (client *Client) azureBatchImageGenerationInternalCreateRequest(ctx context return nil, err } reqQP := req.Raw().URL.Query() - reqQP.Set("api-version", "2023-08-01-preview") + reqQP.Set("api-version", "2023-09-01-preview") req.Raw().URL.RawQuery = reqQP.Encode() req.Raw().Header["Accept"] = []string{"application/json"} if err := runtime.MarshalAsJSON(req, body); err != nil { @@ -81,11 +81,119 @@ func (client *Client) azureBatchImageGenerationInternalCreateRequest(ctx context return req, nil } +// getAudioTranscriptionInternal - Gets transcribed text and associated metadata from provided spoken audio data. Audio will +// be transcribed in the written language corresponding to the language it was spoken in. Gets transcribed text +// and associated metadata from provided spoken audio data. Audio will be transcribed in the written language corresponding +// to the language it was spoken in. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2023-09-01-preview +// - file - The audio data to transcribe. This must be the binary content of a file in one of the supported media formats: flac, +// mp3, mp4, mpeg, mpga, m4a, ogg, wav, webm. +// - options - getAudioTranscriptionInternalOptions contains the optional parameters for the Client.getAudioTranscriptionInternal +// method. +func (client *Client) getAudioTranscriptionInternal(ctx context.Context, file []byte, options *getAudioTranscriptionInternalOptions) (getAudioTranscriptionInternalResponse, error) { + var err error + req, err := client.getAudioTranscriptionInternalCreateRequest(ctx, file, options) + if err != nil { + return getAudioTranscriptionInternalResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return getAudioTranscriptionInternalResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK) { + err = client.newError(httpResp) + return getAudioTranscriptionInternalResponse{}, err + } + resp, err := getAudioTranscriptionInternalHandleResponse(httpResp) + return resp, err +} + +// getAudioTranscriptionInternalCreateRequest creates the getAudioTranscriptionInternal request. +func (client *Client) getAudioTranscriptionInternalCreateRequest(ctx context.Context, file []byte, body *getAudioTranscriptionInternalOptions) (*policy.Request, error) { + urlPath := "audio/transcriptions" + req, err := runtime.NewRequest(ctx, http.MethodPost, client.formatURL(urlPath, getDeployment(body))) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2023-09-01-preview") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + if err := setMultipartFormData(req, file, *body); err != nil { + return nil, err + } + return req, nil +} + +// getAudioTranscriptionInternalHandleResponse handles the getAudioTranscriptionInternal response. +func (client *Client) getAudioTranscriptionInternalHandleResponse(resp *http.Response) (getAudioTranscriptionInternalResponse, error) { + result := getAudioTranscriptionInternalResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.AudioTranscription); err != nil { + return getAudioTranscriptionInternalResponse{}, err + } + return result, nil +} + +// getAudioTranslationInternal - Gets English language transcribed text and associated metadata from provided spoken audio +// data. Gets English language transcribed text and associated metadata from provided spoken audio data. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2023-09-01-preview +// - file - The audio data to translate. This must be the binary content of a file in one of the supported media formats: flac, +// mp3, mp4, mpeg, mpga, m4a, ogg, wav, webm. +// - options - getAudioTranslationInternalOptions contains the optional parameters for the Client.getAudioTranslationInternal +// method. +func (client *Client) getAudioTranslationInternal(ctx context.Context, file []byte, options *getAudioTranslationInternalOptions) (getAudioTranslationInternalResponse, error) { + var err error + req, err := client.getAudioTranslationInternalCreateRequest(ctx, file, options) + if err != nil { + return getAudioTranslationInternalResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return getAudioTranslationInternalResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK) { + err = client.newError(httpResp) + return getAudioTranslationInternalResponse{}, err + } + resp, err := getAudioTranslationInternalHandleResponse(httpResp) + return resp, err +} + +// getAudioTranslationInternalCreateRequest creates the getAudioTranslationInternal request. +func (client *Client) getAudioTranslationInternalCreateRequest(ctx context.Context, file []byte, body *getAudioTranslationInternalOptions) (*policy.Request, error) { + urlPath := "audio/translations" + req, err := runtime.NewRequest(ctx, http.MethodPost, client.formatURL(urlPath, getDeployment(body))) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2023-09-01-preview") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + if err := setMultipartFormData(req, file, *body); err != nil { + return nil, err + } + return req, nil +} + +// getAudioTranslationInternalHandleResponse handles the getAudioTranslationInternal response. +func (client *Client) getAudioTranslationInternalHandleResponse(resp *http.Response) (getAudioTranslationInternalResponse, error) { + result := getAudioTranslationInternalResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.AudioTranscription); err != nil { + return getAudioTranslationInternalResponse{}, err + } + return result, nil +} + // getChatCompletions - Gets chat completions for the provided chat messages. Completions support a wide variety of tasks // and generate text that continues from or "completes" provided prompt data. // If the operation fails it returns an *azcore.ResponseError type. // -// Generated from API version 2023-08-01-preview +// Generated from API version 2023-09-01-preview // - options - GetChatCompletionsOptions contains the optional parameters for the Client.getChatCompletions method. func (client *Client) getChatCompletions(ctx context.Context, body ChatCompletionsOptions, options *GetChatCompletionsOptions) (GetChatCompletionsResponse, error) { var err error @@ -113,7 +221,7 @@ func (client *Client) getChatCompletionsCreateRequest(ctx context.Context, body return nil, err } reqQP := req.Raw().URL.Query() - reqQP.Set("api-version", "2023-08-01-preview") + reqQP.Set("api-version", "2023-09-01-preview") req.Raw().URL.RawQuery = reqQP.Encode() req.Raw().Header["Accept"] = []string{"application/json"} if err := runtime.MarshalAsJSON(req, body); err != nil { @@ -136,7 +244,7 @@ func (client *Client) getChatCompletionsHandleResponse(resp *http.Response) (Get // chat completions capabilities. // If the operation fails it returns an *azcore.ResponseError type. // -// Generated from API version 2023-08-01-preview +// Generated from API version 2023-09-01-preview // - options - GetChatCompletionsWithAzureExtensionsOptions contains the optional parameters for the Client.GetChatCompletionsWithAzureExtensions // method. func (client *Client) getChatCompletionsWithAzureExtensions(ctx context.Context, body ChatCompletionsOptions, options *GetChatCompletionsWithAzureExtensionsOptions) (GetChatCompletionsWithAzureExtensionsResponse, error) { @@ -165,7 +273,7 @@ func (client *Client) getChatCompletionsWithAzureExtensionsCreateRequest(ctx con return nil, err } reqQP := req.Raw().URL.Query() - reqQP.Set("api-version", "2023-08-01-preview") + reqQP.Set("api-version", "2023-09-01-preview") req.Raw().URL.RawQuery = reqQP.Encode() req.Raw().Header["Accept"] = []string{"application/json"} if err := runtime.MarshalAsJSON(req, body); err != nil { @@ -187,7 +295,7 @@ func (client *Client) getChatCompletionsWithAzureExtensionsHandleResponse(resp * // text that continues from or "completes" provided prompt data. // If the operation fails it returns an *azcore.ResponseError type. // -// Generated from API version 2023-08-01-preview +// Generated from API version 2023-09-01-preview // - options - GetCompletionsOptions contains the optional parameters for the Client.GetCompletions method. func (client *Client) GetCompletions(ctx context.Context, body CompletionsOptions, options *GetCompletionsOptions) (GetCompletionsResponse, error) { var err error @@ -215,7 +323,7 @@ func (client *Client) getCompletionsCreateRequest(ctx context.Context, body Comp return nil, err } reqQP := req.Raw().URL.Query() - reqQP.Set("api-version", "2023-08-01-preview") + reqQP.Set("api-version", "2023-09-01-preview") req.Raw().URL.RawQuery = reqQP.Encode() req.Raw().Header["Accept"] = []string{"application/json"} if err := runtime.MarshalAsJSON(req, body); err != nil { @@ -236,7 +344,7 @@ func (client *Client) getCompletionsHandleResponse(resp *http.Response) (GetComp // GetEmbeddings - Return the embeddings for a given prompt. // If the operation fails it returns an *azcore.ResponseError type. // -// Generated from API version 2023-08-01-preview +// Generated from API version 2023-09-01-preview // - options - GetEmbeddingsOptions contains the optional parameters for the Client.GetEmbeddings method. func (client *Client) GetEmbeddings(ctx context.Context, body EmbeddingsOptions, options *GetEmbeddingsOptions) (GetEmbeddingsResponse, error) { var err error @@ -264,7 +372,7 @@ func (client *Client) getEmbeddingsCreateRequest(ctx context.Context, body Embed return nil, err } reqQP := req.Raw().URL.Query() - reqQP.Set("api-version", "2023-08-01-preview") + reqQP.Set("api-version", "2023-09-01-preview") req.Raw().URL.RawQuery = reqQP.Encode() req.Raw().Header["Accept"] = []string{"application/json"} if err := runtime.MarshalAsJSON(req, body); err != nil { diff --git a/sdk/ai/azopenai/client_audio_internal_test.go b/sdk/ai/azopenai/client_audio_internal_test.go new file mode 100644 index 000000000000..37eccb68334b --- /dev/null +++ b/sdk/ai/azopenai/client_audio_internal_test.go @@ -0,0 +1,133 @@ +//go:build go1.18 +// +build go1.18 + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +package azopenai + +import ( + "bytes" + "context" + "errors" + "io" + "mime" + "mime/multipart" + "testing" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/stretchr/testify/require" +) + +func TestSetMultipartFormData(t *testing.T) { + t.Run("getAudioTranscriptionInternalOptions", func(t *testing.T) { + req, err := runtime.NewRequest(context.Background(), "POST", "http://localhost") + require.NoError(t, err) + + err = setMultipartFormData(req, []byte{1, 2, 3}, getAudioTranscriptionInternalOptions{ + Language: to.Ptr("en"), + Model: to.Ptr("hello"), + Prompt: to.Ptr("my prompt"), + ResponseFormat: to.Ptr(AudioTranscriptionFormatJSON), + Temperature: to.Ptr[float32](1.0), + }) + require.NoError(t, err) + + _, params, err := mime.ParseMediaType(req.Raw().Header["Content-Type"][0]) + require.NoError(t, err) + + parts := getParts(t, req.Body(), params["boundary"]) + + require.Equal(t, []kv{ + {Key: "file", Value: "\x01\x02\x03"}, + {Key: "language", Value: "en"}, + {Key: "model", Value: "hello"}, + {Key: "prompt", Value: "my prompt"}, + {Key: "response_format", Value: string(AudioTranscriptionFormatJSON)}, + {Key: "temperature", Value: "1.000000"}, + }, parts) + }) + + t.Run("getAudioTranslationInternalOptions", func(t *testing.T) { + req, err := runtime.NewRequest(context.Background(), "POST", "http://localhost") + require.NoError(t, err) + + err = setMultipartFormData(req, []byte{1, 2, 3}, getAudioTranslationInternalOptions{ + Model: to.Ptr("hello"), + Prompt: to.Ptr("my prompt"), + ResponseFormat: to.Ptr(AudioTranslationFormatJSON), + Temperature: to.Ptr[float32](1.0), + }) + require.NoError(t, err) + + _, params, err := mime.ParseMediaType(req.Raw().Header["Content-Type"][0]) + require.NoError(t, err) + + parts := getParts(t, req.Body(), params["boundary"]) + + require.Equal(t, []kv{ + {Key: "file", Value: "\x01\x02\x03"}, + {Key: "model", Value: "hello"}, + {Key: "prompt", Value: "my prompt"}, + {Key: "response_format", Value: string(AudioTranscriptionFormatJSON)}, + {Key: "temperature", Value: "1.000000"}, + }, parts) + }) +} + +func TestWriteField(t *testing.T) { + buff := &bytes.Buffer{} + writer := multipart.NewWriter(buff) + + // value is nil - we skip the field + err := writeField(writer, "mynil", (*string)(nil)) + require.NoError(t, err) + + err = writeField(writer, "mystring", to.Ptr("hello world")) + require.NoError(t, err) + + err = writeField(writer, "myfloat", to.Ptr[float32](1.0)) + require.NoError(t, err) + + err = writeField(writer, "myformatsrt", to.Ptr(AudioTranscriptionFormatSrt)) + require.NoError(t, err) + + err = writer.Close() + require.NoError(t, err) + + parts := getParts(t, buff, writer.Boundary()) + require.Equal(t, []kv{ + // ...nil field was skipped... + {Key: "mystring", Value: "hello world"}, + {Key: "myfloat", Value: "1.000000"}, + {Key: "myformatsrt", Value: string(AudioTranscriptionFormatSrt)}, + }, parts) +} + +type kv struct { + Key string + Value string +} + +func getParts(t *testing.T, buff io.Reader, boundary string) []kv { + reader := multipart.NewReader(buff, boundary) + + var parts []kv + + for { + part, err := reader.NextPart() + + if errors.Is(err, io.EOF) { + break + } + require.NoError(t, err) + + text, err := io.ReadAll(part) + require.NoError(t, err) + + parts = append(parts, kv{Key: part.FormName(), Value: string(text)}) + } + + return parts +} diff --git a/sdk/ai/azopenai/client_audio_test.go b/sdk/ai/azopenai/client_audio_test.go new file mode 100644 index 000000000000..c454c9924c01 --- /dev/null +++ b/sdk/ai/azopenai/client_audio_test.go @@ -0,0 +1,230 @@ +//go:build go1.18 +// +build go1.18 + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +package azopenai_test + +import ( + "context" + "os" + "testing" + + "github.com/Azure/azure-sdk-for-go/sdk/ai/azopenai" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/Azure/azure-sdk-for-go/sdk/internal/recording" + "github.com/stretchr/testify/require" +) + +func TestClient_GetAudioTranscription_AzureOpenAI(t *testing.T) { + if recording.GetRecordMode() != recording.LiveMode { + t.Skipf("Recording needs to be revisited for multipart: https://github.com/Azure/azure-sdk-for-go/issues/21598") + } + + client := newTestClient(t, azureWhisper) + runTranscriptionTests(t, client, azureWhisperModel) +} + +func TestClient_GetAudioTranscription_OpenAI(t *testing.T) { + if recording.GetRecordMode() != recording.LiveMode { + t.Skipf("Recording needs to be revisited for multipart: https://github.com/Azure/azure-sdk-for-go/issues/21598") + } + + client := newOpenAIClientForTest(t) + + mp3Bytes, err := os.ReadFile(`testdata/sampledata_audiofiles_myVoiceIsMyPassportVerifyMe01.mp3`) + require.NoError(t, err) + + args := newTranscriptionOptions(azopenai.AudioTranscriptionFormatVerboseJSON, openAIWhisperModel, mp3Bytes) + transcriptResp, err := client.GetAudioTranscription(context.Background(), args, nil) + require.NoError(t, err) + require.NotEmpty(t, transcriptResp) + + require.NotEmpty(t, *transcriptResp.Text) + require.Greater(t, *transcriptResp.Duration, float32(0.0)) + require.NotEmpty(t, *transcriptResp.Language) + require.NotEmpty(t, transcriptResp.Segments) + require.NotEmpty(t, transcriptResp.Segments[0]) + require.NotEmpty(t, transcriptResp.Task) +} + +func TestClient_GetAudioTranslation_AzureOpenAI(t *testing.T) { + if recording.GetRecordMode() != recording.LiveMode { + t.Skipf("Recording needs to be revisited for multipart: https://github.com/Azure/azure-sdk-for-go/issues/21598") + } + + client := newTestClient(t, azureWhisper) + runTranslationTests(t, client, azureWhisperModel) +} + +func TestClient_GetAudioTranslation_OpenAI(t *testing.T) { + if recording.GetRecordMode() != recording.LiveMode { + t.Skipf("Recording needs to be revisited for multipart: https://github.com/Azure/azure-sdk-for-go/issues/21598") + } + + client := newOpenAIClientForTest(t) + + mp3Bytes, err := os.ReadFile(`testdata/sampledata_audiofiles_myVoiceIsMyPassportVerifyMe01.mp3`) + require.NoError(t, err) + + args := newTranslationOptions(azopenai.AudioTranslationFormatVerboseJSON, openAIWhisperModel, mp3Bytes) + transcriptResp, err := client.GetAudioTranslation(context.Background(), args, nil) + require.NoError(t, err) + require.NotEmpty(t, transcriptResp) + + require.NotEmpty(t, *transcriptResp.Text) + require.Greater(t, *transcriptResp.Duration, float32(0.0)) + require.NotEmpty(t, *transcriptResp.Language) + require.NotEmpty(t, transcriptResp.Segments) + require.NotEmpty(t, transcriptResp.Segments[0]) + require.NotEmpty(t, transcriptResp.Task) +} + +func runTranscriptionTests(t *testing.T, client *azopenai.Client, model string) { + mp3Bytes, err := os.ReadFile(`testdata/sampledata_audiofiles_myVoiceIsMyPassportVerifyMe01.mp3`) + require.NoError(t, err) + + t.Run(string(azopenai.AudioTranscriptionFormatText), func(t *testing.T) { + args := newTranscriptionOptions(azopenai.AudioTranscriptionFormatText, model, mp3Bytes) + transcriptResp, err := client.GetAudioTranscription(context.Background(), args, nil) + require.NoError(t, err) + require.NotEmpty(t, transcriptResp) + + require.NotEmpty(t, *transcriptResp.Text) + requireEmptyAudioTranscription(t, transcriptResp.AudioTranscription) + }) + + t.Run(string(azopenai.AudioTranscriptionFormatSrt), func(t *testing.T) { + args := newTranscriptionOptions(azopenai.AudioTranscriptionFormatSrt, model, mp3Bytes) + transcriptResp, err := client.GetAudioTranscription(context.Background(), args, nil) + require.NoError(t, err) + require.NotEmpty(t, transcriptResp) + + require.NotEmpty(t, *transcriptResp.Text) + requireEmptyAudioTranscription(t, transcriptResp.AudioTranscription) + }) + + t.Run(string(azopenai.AudioTranscriptionFormatVtt), func(t *testing.T) { + args := newTranscriptionOptions(azopenai.AudioTranscriptionFormatVtt, model, mp3Bytes) + transcriptResp, err := client.GetAudioTranscription(context.Background(), args, nil) + require.NoError(t, err) + require.NotEmpty(t, transcriptResp) + + require.NotEmpty(t, *transcriptResp.Text) + requireEmptyAudioTranscription(t, transcriptResp.AudioTranscription) + }) + + t.Run(string(azopenai.AudioTranscriptionFormatVerboseJSON), func(t *testing.T) { + args := newTranscriptionOptions(azopenai.AudioTranscriptionFormatVerboseJSON, model, mp3Bytes) + transcriptResp, err := client.GetAudioTranscription(context.Background(), args, nil) + require.NoError(t, err) + require.NotEmpty(t, transcriptResp) + + require.NotEmpty(t, *transcriptResp.Text) + require.Greater(t, *transcriptResp.Duration, float32(0.0)) + require.NotEmpty(t, *transcriptResp.Language) + require.NotEmpty(t, transcriptResp.Segments) + require.NotEmpty(t, transcriptResp.Segments[0]) + require.NotEmpty(t, transcriptResp.Task) + }) + + t.Run(string(azopenai.AudioTranscriptionFormatJSON), func(t *testing.T) { + args := newTranscriptionOptions(azopenai.AudioTranscriptionFormatJSON, model, mp3Bytes) + transcriptResp, err := client.GetAudioTranscription(context.Background(), args, nil) + require.NoError(t, err) + require.NotEmpty(t, transcriptResp) + + require.NotEmpty(t, *transcriptResp.Text) + requireEmptyAudioTranscription(t, transcriptResp.AudioTranscription) + }) +} + +func runTranslationTests(t *testing.T, client *azopenai.Client, model string) { + mp3Bytes, err := os.ReadFile(`testdata/sampledata_audiofiles_myVoiceIsMyPassportVerifyMe01.mp3`) + require.NoError(t, err) + + t.Run(string(azopenai.AudioTranscriptionFormatText), func(t *testing.T) { + args := newTranslationOptions(azopenai.AudioTranslationFormatText, model, mp3Bytes) + transcriptResp, err := client.GetAudioTranslation(context.Background(), args, nil) + require.NoError(t, err) + require.NotEmpty(t, transcriptResp) + + require.NotEmpty(t, *transcriptResp.Text) + requireEmptyAudioTranscription(t, transcriptResp.AudioTranscription) + }) + + t.Run(string(azopenai.AudioTranscriptionFormatSrt), func(t *testing.T) { + args := newTranslationOptions(azopenai.AudioTranslationFormatSrt, model, mp3Bytes) + transcriptResp, err := client.GetAudioTranslation(context.Background(), args, nil) + require.NoError(t, err) + require.NotEmpty(t, transcriptResp) + + require.NotEmpty(t, *transcriptResp.Text) + requireEmptyAudioTranscription(t, transcriptResp.AudioTranscription) + }) + + t.Run(string(azopenai.AudioTranscriptionFormatVtt), func(t *testing.T) { + args := newTranslationOptions(azopenai.AudioTranslationFormatVtt, model, mp3Bytes) + transcriptResp, err := client.GetAudioTranslation(context.Background(), args, nil) + require.NoError(t, err) + require.NotEmpty(t, transcriptResp) + + require.NotEmpty(t, *transcriptResp.Text) + requireEmptyAudioTranscription(t, transcriptResp.AudioTranscription) + }) + + t.Run(string(azopenai.AudioTranscriptionFormatVerboseJSON), func(t *testing.T) { + args := newTranslationOptions(azopenai.AudioTranslationFormatVerboseJSON, model, mp3Bytes) + transcriptResp, err := client.GetAudioTranslation(context.Background(), args, nil) + require.NoError(t, err) + require.NotEmpty(t, transcriptResp) + + require.NotEmpty(t, *transcriptResp.Text) + require.Greater(t, *transcriptResp.Duration, float32(0.0)) + require.NotEmpty(t, *transcriptResp.Language) + require.NotEmpty(t, transcriptResp.Segments) + require.NotEmpty(t, transcriptResp.Segments[0]) + require.NotEmpty(t, transcriptResp.Task) + }) + + t.Run(string(azopenai.AudioTranscriptionFormatJSON), func(t *testing.T) { + args := newTranslationOptions(azopenai.AudioTranslationFormatJSON, model, mp3Bytes) + transcriptResp, err := client.GetAudioTranslation(context.Background(), args, nil) + require.NoError(t, err) + require.NotEmpty(t, transcriptResp) + + require.NotEmpty(t, *transcriptResp.Text) + requireEmptyAudioTranscription(t, transcriptResp.AudioTranscription) + }) +} + +func newTranscriptionOptions(format azopenai.AudioTranscriptionFormat, model string, mp3Bytes []byte) azopenai.AudioTranscriptionOptions { + return azopenai.AudioTranscriptionOptions{ + Deployment: model, + File: mp3Bytes, + ResponseFormat: &format, + Language: to.Ptr("en"), + Temperature: to.Ptr[float32](0.0), + } +} + +func newTranslationOptions(format azopenai.AudioTranslationFormat, model string, mp3Bytes []byte) azopenai.AudioTranslationOptions { + return azopenai.AudioTranslationOptions{ + Deployment: model, + File: mp3Bytes, + ResponseFormat: &format, + Temperature: to.Ptr[float32](0.0), + } +} + +// requireEmptyAudioTranscription checks that all the attributes are empty (aside +// from Text) +func requireEmptyAudioTranscription(t *testing.T, at azopenai.AudioTranscription) { + // Text is always filled out for + + require.Empty(t, at.Duration) + require.Empty(t, at.Language) + require.Empty(t, at.Segments) + require.Empty(t, at.Task) +} diff --git a/sdk/ai/azopenai/client_chat_completions_extensions_test.go b/sdk/ai/azopenai/client_chat_completions_extensions_test.go index ce1d8f7062b1..5a79870dc8aa 100644 --- a/sdk/ai/azopenai/client_chat_completions_extensions_test.go +++ b/sdk/ai/azopenai/client_chat_completions_extensions_test.go @@ -48,7 +48,7 @@ func TestChatCompletions_extensions_bringYourOwnData(t *testing.T) { } func TestChatExtensionsStreaming_extensions_bringYourOwnData(t *testing.T) { - client := newAzureOpenAIClientForTest(t, azureOpenAI) + client := newAzureOpenAIClientForTest(t, azureOpenAICanary) streamResp, err := client.GetChatCompletionsStream(context.Background(), azopenai.ChatCompletionsOptions{ Messages: []azopenai.ChatMessage{ @@ -59,7 +59,7 @@ func TestChatExtensionsStreaming_extensions_bringYourOwnData(t *testing.T) { Extensions: []azopenai.AzureChatExtensionConfiguration{ { Type: to.Ptr(azopenai.AzureChatExtensionTypeAzureCognitiveSearch), - Parameters: azureOpenAI.Cognitive, + Parameters: azureOpenAICanary.Cognitive, }, }, }, diff --git a/sdk/ai/azopenai/client_chat_completions_test.go b/sdk/ai/azopenai/client_chat_completions_test.go index 95758f4dcd69..6ae87db95221 100644 --- a/sdk/ai/azopenai/client_chat_completions_test.go +++ b/sdk/ai/azopenai/client_chat_completions_test.go @@ -40,13 +40,8 @@ var expectedContent = "1, 2, 3, 4, 5, 6, 7, 8, 9, 10." var expectedRole = azopenai.ChatRoleAssistant func TestClient_GetChatCompletions(t *testing.T) { - cred, err := azopenai.NewKeyCredential(azureOpenAI.APIKey) - require.NoError(t, err) - - chatClient, err := azopenai.NewClientWithKeyCredential(azureOpenAI.Endpoint, cred, newClientOptionsForTest(t)) - require.NoError(t, err) - - testGetChatCompletions(t, chatClient, azureOpenAI) + client := newTestClient(t, azureOpenAI.Endpoint) + testGetChatCompletions(t, client, azureOpenAI) } func TestClient_GetChatCompletionsStream(t *testing.T) { @@ -96,10 +91,10 @@ func testGetChatCompletions(t *testing.T, client *azopenai.Client, tv testVars) resp, err := client.GetChatCompletions(context.Background(), newTestChatCompletionOptions(tv), nil) require.NoError(t, err) - if tv.Azure { + if tv.Endpoint.Azure { // Azure also provides content-filtering. This particular prompt and responses // will be considered safe. - expected.PromptAnnotations = []azopenai.PromptFilterResult{ + expected.PromptFilterResults = []azopenai.PromptFilterResult{ {PromptIndex: to.Ptr[int32](0), ContentFilterResults: (*azopenai.PromptFilterResultContentFilterResults)(safeContentFilter)}, } expected.Choices[0].ContentFilterResults = safeContentFilter @@ -133,10 +128,10 @@ func testGetChatCompletionsStream(t *testing.T, client *azopenai.Client, tv test require.NoError(t, err) - if completion.PromptAnnotations != nil { + if completion.PromptFilterResults != nil { require.Equal(t, []azopenai.PromptFilterResult{ {PromptIndex: to.Ptr[int32](0), ContentFilterResults: (*azopenai.PromptFilterResultContentFilterResults)(safeContentFilter)}, - }, completion.PromptAnnotations) + }, completion.PromptFilterResults) } if len(completion.Choices) == 0 { @@ -180,7 +175,7 @@ func TestClient_GetChatCompletions_DefaultAzureCredential(t *testing.T) { }) require.NoError(t, err) - chatClient, err := azopenai.NewClient(azureOpenAI.Endpoint, dac, &azopenai.ClientOptions{ + chatClient, err := azopenai.NewClient(azureOpenAI.Endpoint.URL, dac, &azopenai.ClientOptions{ ClientOptions: policy.ClientOptions{Transport: recordingTransporter}, }) require.NoError(t, err) @@ -189,13 +184,9 @@ func TestClient_GetChatCompletions_DefaultAzureCredential(t *testing.T) { } func TestClient_GetChatCompletions_InvalidModel(t *testing.T) { - cred, err := azopenai.NewKeyCredential(azureOpenAI.APIKey) - require.NoError(t, err) - - chatClient, err := azopenai.NewClientWithKeyCredential(azureOpenAI.Endpoint, cred, newClientOptionsForTest(t)) - require.NoError(t, err) + client := newTestClient(t, azureOpenAI.Endpoint) - _, err = chatClient.GetChatCompletions(context.Background(), azopenai.ChatCompletionsOptions{ + _, err := client.GetChatCompletions(context.Background(), azopenai.ChatCompletionsOptions{ Messages: []azopenai.ChatMessage{ { Role: to.Ptr(azopenai.ChatRole("user")), diff --git a/sdk/ai/azopenai/client_completions_test.go b/sdk/ai/azopenai/client_completions_test.go index 9496ad11308a..53a43f1529e8 100644 --- a/sdk/ai/azopenai/client_completions_test.go +++ b/sdk/ai/azopenai/client_completions_test.go @@ -16,12 +16,7 @@ import ( ) func TestClient_GetCompletions_AzureOpenAI(t *testing.T) { - cred, err := azopenai.NewKeyCredential(azureOpenAI.APIKey) - require.NoError(t, err) - - client, err := azopenai.NewClientWithKeyCredential(azureOpenAI.Endpoint, cred, newClientOptionsForTest(t)) - require.NoError(t, err) - + client := newTestClient(t, azureOpenAI.Endpoint) testGetCompletions(t, client, true) } @@ -69,7 +64,7 @@ func testGetCompletions(t *testing.T, client *azopenai.Client, isAzure bool) { if isAzure { want.Choices[0].ContentFilterResults = (*azopenai.ChoiceContentFilterResults)(safeContentFilter) - want.PromptAnnotations = []azopenai.PromptFilterResult{ + want.PromptFilterResults = []azopenai.PromptFilterResult{ {PromptIndex: to.Ptr[int32](0), ContentFilterResults: (*azopenai.PromptFilterResultContentFilterResults)(safeContentFilter)}, } } diff --git a/sdk/ai/azopenai/client_embeddings_test.go b/sdk/ai/azopenai/client_embeddings_test.go index 388f1e9a0db8..b407d510afa4 100644 --- a/sdk/ai/azopenai/client_embeddings_test.go +++ b/sdk/ai/azopenai/client_embeddings_test.go @@ -13,13 +13,9 @@ import ( ) func TestClient_GetEmbeddings_InvalidModel(t *testing.T) { - cred, err := azopenai.NewKeyCredential(azureOpenAI.APIKey) - require.NoError(t, err) + client := newTestClient(t, azureOpenAI.Endpoint) - chatClient, err := azopenai.NewClientWithKeyCredential(azureOpenAI.Endpoint, cred, newClientOptionsForTest(t)) - require.NoError(t, err) - - _, err = chatClient.GetEmbeddings(context.Background(), azopenai.EmbeddingsOptions{ + _, err := client.GetEmbeddings(context.Background(), azopenai.EmbeddingsOptions{ Deployment: "thisdoesntexist", }, nil) @@ -38,12 +34,7 @@ func TestClient_OpenAI_GetEmbeddings(t *testing.T) { } func TestClient_GetEmbeddings(t *testing.T) { - cred, err := azopenai.NewKeyCredential(azureOpenAI.APIKey) - require.NoError(t, err) - - client, err := azopenai.NewClientWithKeyCredential(azureOpenAI.Endpoint, cred, newClientOptionsForTest(t)) - require.NoError(t, err) - + client := newTestClient(t, azureOpenAI.Endpoint) testGetEmbeddings(t, client, azureOpenAI.Embeddings) } diff --git a/sdk/ai/azopenai/client_rai_test.go b/sdk/ai/azopenai/client_rai_test.go index 1569f7637faa..fa6f239376fe 100644 --- a/sdk/ai/azopenai/client_rai_test.go +++ b/sdk/ai/azopenai/client_rai_test.go @@ -22,7 +22,7 @@ func TestClient_GetCompletions_AzureOpenAI_ContentFilter_Response(t *testing.T) client := newAzureOpenAIClientForTest(t, azureOpenAI) resp, err := client.GetCompletions(context.Background(), azopenai.CompletionsOptions{ - Prompt: []string{"How do I rob a bank?"}, + Prompt: []string{"How do I rob a bank with violence?"}, MaxTokens: to.Ptr(int32(2048 - 127)), Temperature: to.Ptr(float32(0.0)), Deployment: azureOpenAI.Completions, @@ -38,7 +38,7 @@ func TestClient_GetChatCompletions_AzureOpenAI_ContentFilterWithError(t *testing resp, err := client.GetChatCompletions(context.Background(), azopenai.ChatCompletionsOptions{ Messages: []azopenai.ChatMessage{ {Role: to.Ptr(azopenai.ChatRoleSystem), Content: to.Ptr("You are a helpful assistant.")}, - {Role: to.Ptr(azopenai.ChatRoleUser), Content: to.Ptr("How do I rob a bank?")}, + {Role: to.Ptr(azopenai.ChatRoleUser), Content: to.Ptr("How do I rob a bank with violence?")}, }, MaxTokens: to.Ptr(int32(2048 - 127)), Temperature: to.Ptr(float32(0.0)), @@ -78,16 +78,16 @@ func assertContentFilterError(t *testing.T, err error, requireAnnotations bool) require.ErrorAs(t, err, &contentFilterErr) if requireAnnotations { - require.Equal(t, &azopenai.ContentFilterResultsHate{Filtered: to.Ptr(false), Severity: to.Ptr(azopenai.ContentFilterSeveritySafe)}, contentFilterErr.ContentFilterResults.Hate) - require.Equal(t, &azopenai.ContentFilterResultsSelfHarm{Filtered: to.Ptr(false), Severity: to.Ptr(azopenai.ContentFilterSeveritySafe)}, contentFilterErr.ContentFilterResults.SelfHarm) - require.Equal(t, &azopenai.ContentFilterResultsSexual{Filtered: to.Ptr(false), Severity: to.Ptr(azopenai.ContentFilterSeveritySafe)}, contentFilterErr.ContentFilterResults.Sexual) - require.Equal(t, &azopenai.ContentFilterResultsViolence{Filtered: to.Ptr(true), Severity: to.Ptr(azopenai.ContentFilterSeverityMedium)}, contentFilterErr.ContentFilterResults.Violence) + require.Equal(t, &azopenai.ContentFilterResult{Filtered: to.Ptr(false), Severity: to.Ptr(azopenai.ContentFilterSeveritySafe)}, contentFilterErr.ContentFilterResults.Hate) + require.Equal(t, &azopenai.ContentFilterResult{Filtered: to.Ptr(false), Severity: to.Ptr(azopenai.ContentFilterSeveritySafe)}, contentFilterErr.ContentFilterResults.SelfHarm) + require.Equal(t, &azopenai.ContentFilterResult{Filtered: to.Ptr(false), Severity: to.Ptr(azopenai.ContentFilterSeveritySafe)}, contentFilterErr.ContentFilterResults.Sexual) + require.Equal(t, &azopenai.ContentFilterResult{Filtered: to.Ptr(true), Severity: to.Ptr(azopenai.ContentFilterSeverityMedium)}, contentFilterErr.ContentFilterResults.Violence) } } var safeContentFilter = &azopenai.ChatChoiceContentFilterResults{ - Hate: &azopenai.ContentFilterResultsHate{Filtered: to.Ptr(false), Severity: to.Ptr(azopenai.ContentFilterSeveritySafe)}, - SelfHarm: &azopenai.ContentFilterResultsSelfHarm{Filtered: to.Ptr(false), Severity: to.Ptr(azopenai.ContentFilterSeveritySafe)}, - Sexual: &azopenai.ContentFilterResultsSexual{Filtered: to.Ptr(false), Severity: to.Ptr(azopenai.ContentFilterSeveritySafe)}, - Violence: &azopenai.ContentFilterResultsViolence{Filtered: to.Ptr(false), Severity: to.Ptr(azopenai.ContentFilterSeveritySafe)}, + Hate: &azopenai.ContentFilterResult{Filtered: to.Ptr(false), Severity: to.Ptr(azopenai.ContentFilterSeveritySafe)}, + SelfHarm: &azopenai.ContentFilterResult{Filtered: to.Ptr(false), Severity: to.Ptr(azopenai.ContentFilterSeveritySafe)}, + Sexual: &azopenai.ContentFilterResult{Filtered: to.Ptr(false), Severity: to.Ptr(azopenai.ContentFilterSeveritySafe)}, + Violence: &azopenai.ContentFilterResult{Filtered: to.Ptr(false), Severity: to.Ptr(azopenai.ContentFilterSeveritySafe)}, } diff --git a/sdk/ai/azopenai/client_shared_test.go b/sdk/ai/azopenai/client_shared_test.go index 8c5eb56f7821..f963fc413f9f 100644 --- a/sdk/ai/azopenai/client_shared_test.go +++ b/sdk/ai/azopenai/client_shared_test.go @@ -23,59 +23,87 @@ import ( ) var ( - azureOpenAI testVars - azureOpenAICanary testVars - openAI testVars + azureOpenAI testVars + azureOpenAICanary testVars + openAI testVars + azureWhisper endpoint + azureWhisperModel string + openAIWhisper endpoint + openAIWhisperModel string ) -type testVars struct { - Endpoint string // env: AOAI_ENDPOINT, OPENAI_ENDPOINT - APIKey string // env: AOAI_API_KEY, OPENAI_API_KEY - Completions string // env: AOAI_COMPLETIONS_MODEL_DEPLOYMENT, OPENAI_COMPLETIONS_MODEL - ChatCompletions string // env: AOAI_CHAT_COMPLETIONS_MODEL_DEPLOYMENT, OPENAI_CHAT_COMPLETIONS_MODEL - Embeddings string // env: AOAI_EMBEDDINGS_MODEL_DEPLOYMENT, OPENAI_EMBEDDINGS_MODEL - Azure bool - - Cognitive azopenai.AzureCognitiveSearchChatExtensionConfiguration +type endpoint struct { + URL string + APIKey string + Azure bool } -func newTestVars(prefix string, isCanary bool) testVars { - getRequired := func(name string) string { - v := os.Getenv(name) +func newTestClient(t *testing.T, ep endpoint) *azopenai.Client { + if ep.Azure { + cred, err := azopenai.NewKeyCredential(ep.APIKey) + require.NoError(t, err) + + client, err := azopenai.NewClientWithKeyCredential(ep.URL, cred, newClientOptionsForTest(t)) + require.NoError(t, err) - if v == "" { - panic(fmt.Sprintf("Env variable %s is missing", name)) + return client + } else { + if ep.APIKey == "" { + t.Skipf("OPENAI_API_KEY not defined, skipping OpenAI public endpoint test") } - return v - } + cred, err := azopenai.NewKeyCredential(ep.APIKey) + require.NoError(t, err) - azure := prefix == "AOAI" + // we get rate limited quite a bit. + options := newClientOptionsForTest(t) + + if options == nil { + options = &azopenai.ClientOptions{} + } + + options.Retry = policy.RetryOptions{ + MaxRetries: 60, + RetryDelay: time.Second, + MaxRetryDelay: time.Second, + } - canarySuffix := "" - deplSuffix := "" + client, err := azopenai.NewClientForOpenAI(ep.URL, cred, options) + require.NoError(t, err) - if azure { - deplSuffix += "_DEPLOYMENT" + return client } +} + +type testVars struct { + Endpoint endpoint + Completions string + ChatCompletions string + Embeddings string + Cognitive azopenai.AzureCognitiveSearchChatExtensionConfiguration +} + +func newTestVars(prefix string, isCanary bool) testVars { + azure := prefix == "AOAI" + suffix := "" if isCanary { - canarySuffix += "_CANARY" + suffix += "_CANARY" } tv := testVars{ - Endpoint: getRequired(prefix + "_ENDPOINT" + canarySuffix), - APIKey: getRequired(prefix + "_API_KEY" + canarySuffix), - - Completions: getRequired(prefix + "_COMPLETIONS_MODEL" + deplSuffix + canarySuffix), + Endpoint: endpoint{ + URL: getRequired(prefix + "_ENDPOINT" + suffix), + APIKey: getRequired(prefix + "_API_KEY" + suffix), + Azure: azure, + }, + Completions: getRequired(prefix + "_COMPLETIONS_MODEL" + suffix), // ex: gpt-4-0613 - ChatCompletions: getRequired(prefix + "_CHAT_COMPLETIONS_MODEL" + deplSuffix + canarySuffix), + ChatCompletions: getRequired(prefix + "_CHAT_COMPLETIONS_MODEL" + suffix), // ex: embedding - Embeddings: getRequired(prefix + "_EMBEDDINGS_MODEL" + deplSuffix + canarySuffix), - - Azure: azure, + Embeddings: getRequired(prefix + "_EMBEDDINGS_MODEL" + suffix), Cognitive: azopenai.AzureCognitiveSearchChatExtensionConfiguration{ Endpoint: to.Ptr(getRequired("COGNITIVE_SEARCH_API_ENDPOINT")), @@ -84,9 +112,9 @@ func newTestVars(prefix string, isCanary bool) testVars { }, } - if tv.Endpoint != "" && !strings.HasSuffix(tv.Endpoint, "/") { + if tv.Endpoint.URL != "" && !strings.HasSuffix(tv.Endpoint.URL, "/") { // (this just makes recording replacement easier) - tv.Endpoint += "/" + tv.Endpoint.URL += "/" } return tv @@ -99,15 +127,33 @@ const fakeCognitiveIndexName = "index" func initEnvVars() { if recording.GetRecordMode() == recording.PlaybackMode { - azureOpenAI.Azure = true - azureOpenAI.Endpoint = fakeEndpoint - azureOpenAI.APIKey = fakeAPIKey - openAI.APIKey = fakeAPIKey - openAI.Endpoint = fakeEndpoint - - azureOpenAICanary.Azure = true - azureOpenAICanary.Endpoint = fakeEndpoint - azureOpenAICanary.APIKey = fakeAPIKey + azureOpenAI.Endpoint = endpoint{ + URL: fakeEndpoint, + APIKey: fakeAPIKey, + Azure: true, + } + + azureOpenAICanary.Endpoint = endpoint{ + URL: fakeEndpoint, + APIKey: fakeAPIKey, + Azure: true, + } + + azureWhisper = endpoint{ + URL: fakeEndpoint, + APIKey: fakeAPIKey, + Azure: true, + } + + azureWhisperModel = "whisper-deployment" + + openAI.Endpoint = endpoint{ + APIKey: fakeAPIKey, + URL: fakeEndpoint, + } + + openAIWhisperModel = "whisper-1" + azureOpenAICanary.Completions = "" azureOpenAICanary.ChatCompletions = "gpt-4" @@ -125,6 +171,7 @@ func initEnvVars() { IndexName: to.Ptr(fakeCognitiveIndexName), Key: to.Ptr(fakeAPIKey), } + azureOpenAICanary.Cognitive = azureOpenAI.Cognitive } else { if err := godotenv.Load(); err != nil { fmt.Printf("Failed to load .env file: %s\n", err) @@ -133,6 +180,20 @@ func initEnvVars() { azureOpenAI = newTestVars("AOAI", false) azureOpenAICanary = newTestVars("AOAI", true) openAI = newTestVars("OPENAI", false) + + azureWhisper = endpoint{ + URL: getRequired("AOAI_ENDPOINT_WHISPER"), + APIKey: getRequired("AOAI_API_KEY_WHISPER"), + Azure: true, + } + azureWhisperModel = getRequired("AOAI_MODEL_WHISPER") + + openAIWhisper = endpoint{ + URL: getRequired("OPENAI_ENDPOINT"), + APIKey: getRequired("OPENAI_API_KEY"), + Azure: true, + } + openAIWhisperModel = "whisper-1" } } @@ -147,21 +208,24 @@ func newRecordingTransporter(t *testing.T) policy.Transporter { err = recording.AddHeaderRegexSanitizer("Api-Key", fakeAPIKey, "", nil) require.NoError(t, err) - err = recording.AddHeaderRegexSanitizer("User-Agent", "fake-user-agent", ".*", nil) + err = recording.AddHeaderRegexSanitizer("User-Agent", "fake-user-agent", "", nil) require.NoError(t, err) // "RequestUri": "https://openai-shared.openai.azure.com/openai/deployments/text-davinci-003/completions?api-version=2023-03-15-preview", - err = recording.AddURISanitizer(fakeEndpoint, regexp.QuoteMeta(azureOpenAI.Endpoint), nil) + err = recording.AddURISanitizer(fakeEndpoint, regexp.QuoteMeta(azureOpenAI.Endpoint.URL), nil) + require.NoError(t, err) + + err = recording.AddURISanitizer(fakeEndpoint, regexp.QuoteMeta(azureOpenAICanary.Endpoint.URL), nil) require.NoError(t, err) - err = recording.AddURISanitizer(fakeEndpoint, regexp.QuoteMeta(azureOpenAICanary.Endpoint), nil) + err = recording.AddURISanitizer(fakeEndpoint, regexp.QuoteMeta(azureWhisper.URL), nil) require.NoError(t, err) err = recording.AddURISanitizer("/openai/operations/images/00000000-AAAA-BBBB-CCCC-DDDDDDDDDDDD", "/openai/operations/images/[A-Za-z-0-9]+", nil) require.NoError(t, err) - if openAI.Endpoint != "" { - err = recording.AddURISanitizer(fakeEndpoint, regexp.QuoteMeta(openAI.Endpoint), nil) + if openAI.Endpoint.URL != "" { + err = recording.AddURISanitizer(fakeEndpoint, regexp.QuoteMeta(openAI.Endpoint.URL), nil) require.NoError(t, err) } @@ -216,46 +280,19 @@ func newClientOptionsForTest(t *testing.T) *azopenai.ClientOptions { co.Transport = newRecordingTransporter(t) } + // Useful when debugging responses. + //co.Logging.IncludeBody = true return co } // newAzureOpenAIClientForTest can create a client pointing to the "canary" endpoint (basically - leading fixes or features) // or the current deployed endpoint. func newAzureOpenAIClientForTest(t *testing.T, tv testVars) *azopenai.Client { - cred, err := azopenai.NewKeyCredential(tv.APIKey) - require.NoError(t, err) - - client, err := azopenai.NewClientWithKeyCredential(tv.Endpoint, cred, newClientOptionsForTest(t)) - require.NoError(t, err) - - return client + return newTestClient(t, tv.Endpoint) } func newOpenAIClientForTest(t *testing.T) *azopenai.Client { - if openAI.APIKey == "" { - t.Skipf("OPENAI_API_KEY not defined, skipping OpenAI public endpoint test") - } - - cred, err := azopenai.NewKeyCredential(openAI.APIKey) - require.NoError(t, err) - - // we get rate limited quite a bit. - options := newClientOptionsForTest(t) - - if options == nil { - options = &azopenai.ClientOptions{} - } - - options.Retry = policy.RetryOptions{ - MaxRetries: 60, - RetryDelay: time.Second, - MaxRetryDelay: time.Second, - } - - chatClient, err := azopenai.NewClientForOpenAI(openAI.Endpoint, cred, options) - require.NoError(t, err) - - return chatClient + return newTestClient(t, openAI.Endpoint) } // newBogusAzureOpenAIClient creates a client that uses an invalid key, which will cause Azure OpenAI to return @@ -264,8 +301,9 @@ func newBogusAzureOpenAIClient(t *testing.T) *azopenai.Client { cred, err := azopenai.NewKeyCredential("bogus-api-key") require.NoError(t, err) - client, err := azopenai.NewClientWithKeyCredential(azureOpenAI.Endpoint, cred, newClientOptionsForTest(t)) + client, err := azopenai.NewClientWithKeyCredential(azureOpenAI.Endpoint.URL, cred, newClientOptionsForTest(t)) require.NoError(t, err) + return client } @@ -275,7 +313,7 @@ func newBogusOpenAIClient(t *testing.T) *azopenai.Client { cred, err := azopenai.NewKeyCredential("bogus-api-key") require.NoError(t, err) - client, err := azopenai.NewClientForOpenAI(openAI.Endpoint, cred, newClientOptionsForTest(t)) + client, err := azopenai.NewClientForOpenAI(openAI.Endpoint.URL, cred, newClientOptionsForTest(t)) require.NoError(t, err) return client } @@ -289,3 +327,13 @@ func assertResponseIsError(t *testing.T, err error) { // we sometimes get rate limited but (for this kind of test) it's actually okay require.Truef(t, respErr.StatusCode == http.StatusUnauthorized || respErr.StatusCode == http.StatusTooManyRequests, "An acceptable error comes back (actual: %d)", respErr.StatusCode) } + +func getRequired(name string) string { + v := os.Getenv(name) + + if v == "" { + panic(fmt.Sprintf("Env variable %s is missing", name)) + } + + return v +} diff --git a/sdk/ai/azopenai/constants.go b/sdk/ai/azopenai/constants.go index 8a08912bac02..1d30207f5c26 100644 --- a/sdk/ai/azopenai/constants.go +++ b/sdk/ai/azopenai/constants.go @@ -8,6 +8,66 @@ package azopenai +// AudioTaskLabel - Defines the possible descriptors for available audio operation responses. +type AudioTaskLabel string + +const ( + AudioTaskLabelTranscribe AudioTaskLabel = "transcribe" + AudioTaskLabelTranslate AudioTaskLabel = "translate" +) + +// PossibleAudioTaskLabelValues returns the possible values for the AudioTaskLabel const type. +func PossibleAudioTaskLabelValues() []AudioTaskLabel { + return []AudioTaskLabel{ + AudioTaskLabelTranscribe, + AudioTaskLabelTranslate, + } +} + +// AudioTranscriptionFormat - Defines available options for the underlying response format of output transcription information. +type AudioTranscriptionFormat string + +const ( + AudioTranscriptionFormatJSON AudioTranscriptionFormat = "json" + AudioTranscriptionFormatSrt AudioTranscriptionFormat = "srt" + AudioTranscriptionFormatText AudioTranscriptionFormat = "text" + AudioTranscriptionFormatVerboseJSON AudioTranscriptionFormat = "verbose_json" + AudioTranscriptionFormatVtt AudioTranscriptionFormat = "vtt" +) + +// PossibleAudioTranscriptionFormatValues returns the possible values for the AudioTranscriptionFormat const type. +func PossibleAudioTranscriptionFormatValues() []AudioTranscriptionFormat { + return []AudioTranscriptionFormat{ + AudioTranscriptionFormatJSON, + AudioTranscriptionFormatSrt, + AudioTranscriptionFormatText, + AudioTranscriptionFormatVerboseJSON, + AudioTranscriptionFormatVtt, + } +} + +// AudioTranslationFormat - Defines available options for the underlying response format of output translation information. +type AudioTranslationFormat string + +const ( + AudioTranslationFormatJSON AudioTranslationFormat = "json" + AudioTranslationFormatSrt AudioTranslationFormat = "srt" + AudioTranslationFormatText AudioTranslationFormat = "text" + AudioTranslationFormatVerboseJSON AudioTranslationFormat = "verbose_json" + AudioTranslationFormatVtt AudioTranslationFormat = "vtt" +) + +// PossibleAudioTranslationFormatValues returns the possible values for the AudioTranslationFormat const type. +func PossibleAudioTranslationFormatValues() []AudioTranslationFormat { + return []AudioTranslationFormat{ + AudioTranslationFormatJSON, + AudioTranslationFormatSrt, + AudioTranslationFormatText, + AudioTranslationFormatVerboseJSON, + AudioTranslationFormatVtt, + } +} + // AzureChatExtensionType - A representation of configuration data for a single Azure OpenAI chat extension. This will be // used by a chat completions request that should use Azure OpenAI chat extensions to augment the response // behavior. The use of this configuration is compatible only with Azure OpenAI. diff --git a/sdk/ai/azopenai/custom_client.go b/sdk/ai/azopenai/custom_client.go index eacc751341a8..5c195450d950 100644 --- a/sdk/ai/azopenai/custom_client.go +++ b/sdk/ai/azopenai/custom_client.go @@ -194,6 +194,12 @@ func (client *Client) GetChatCompletionsStream(ctx context.Context, body ChatCom if hasAzureExtensions(body) { req, err = client.getChatCompletionsWithAzureExtensionsCreateRequest(ctx, body, &GetChatCompletionsWithAzureExtensionsOptions{}) + + if err == nil { + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2023-08-01-preview") + req.Raw().URL.RawQuery = reqQP.Encode() + } } else { req, err = client.getChatCompletionsCreateRequest(ctx, body, &GetChatCompletionsOptions{}) } @@ -268,7 +274,7 @@ type clientData struct { azure bool } -func getDeployment[T ChatCompletionsOptions | CompletionsOptions | EmbeddingsOptions | ImageGenerationOptions](v T) string { +func getDeployment[T ChatCompletionsOptions | CompletionsOptions | EmbeddingsOptions | ImageGenerationOptions | *getAudioTranscriptionInternalOptions | *getAudioTranslationInternalOptions](v T) string { switch a := any(v).(type) { case ChatCompletionsOptions: return a.Deployment @@ -278,6 +284,10 @@ func getDeployment[T ChatCompletionsOptions | CompletionsOptions | EmbeddingsOpt return a.Deployment case ImageGenerationOptions: return "" + case *getAudioTranscriptionInternalOptions: + return *a.Model + case *getAudioTranslationInternalOptions: + return *a.Model default: return "" } diff --git a/sdk/ai/azopenai/custom_client_audio.go b/sdk/ai/azopenai/custom_client_audio.go new file mode 100644 index 000000000000..c521c27188a1 --- /dev/null +++ b/sdk/ai/azopenai/custom_client_audio.go @@ -0,0 +1,220 @@ +//go:build go1.18 +// +build go1.18 + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +package azopenai + +import ( + "bytes" + "context" + "fmt" + "io" + "mime/multipart" + "net/http" + "strings" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" +) + +// GetAudioTranscriptionOptions contains the optional parameters for the [Client.GetAudioTranscription] method. +type GetAudioTranscriptionOptions struct { + // placeholder for future optional parameters +} + +// GetAudioTranscriptionResponse contains the response from method [Client.GetAudioTranscription]. +type GetAudioTranscriptionResponse struct { + AudioTranscription +} + +// GetAudioTranscription gets transcribed text and associated metadata from provided spoken audio data. Audio will +// be transcribed in the written language corresponding to the language it was spoken in. Gets transcribed text +// and associated metadata from provided spoken audio data. Audio will be transcribed in the written language corresponding +// to the language it was spoken in. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2023-09-01-preview +// - body - contains parameters to specify audio data to transcribe and control the transcription. +// - options - optional parameters for this method. +func (client *Client) GetAudioTranscription(ctx context.Context, body AudioTranscriptionOptions, options *GetAudioTranscriptionOptions) (GetAudioTranscriptionResponse, error) { + resp, err := client.getAudioTranscriptionInternal(ctx, body.File, &getAudioTranscriptionInternalOptions{ + Language: body.Language, + Model: &body.Deployment, + Prompt: body.Prompt, + ResponseFormat: body.ResponseFormat, + Temperature: body.Temperature, + }) + + if err != nil { + return GetAudioTranscriptionResponse{}, err + } + + return GetAudioTranscriptionResponse(resp), nil +} + +// GetAudioTranslationOptions contains the optional parameters for the [Client.GetAudioTranslation] method. +type GetAudioTranslationOptions struct { + // placeholder for future optional parameters +} + +// GetAudioTranslationResponse contains the response from method [Client.GetAudioTranslation]. +type GetAudioTranslationResponse struct { + AudioTranscription +} + +// GetAudioTranslation gets English language transcribed text and associated metadata from provided spoken audio +// data. Gets English language transcribed text and associated metadata from provided spoken audio data. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2023-09-01-preview +// - body - contains parameters to specify audio data to translate and control the translation. +// - options - optional parameters for this method. +func (client *Client) GetAudioTranslation(ctx context.Context, body AudioTranslationOptions, options *GetAudioTranslationOptions) (GetAudioTranslationResponse, error) { + resp, err := client.getAudioTranslationInternal(ctx, body.File, &getAudioTranslationInternalOptions{ + Model: &body.Deployment, + Prompt: body.Prompt, + ResponseFormat: body.ResponseFormat, + Temperature: body.Temperature, + }) + + if err != nil { + return GetAudioTranslationResponse{}, err + } + + return GetAudioTranslationResponse(resp), nil +} + +func setMultipartFormData[T getAudioTranscriptionInternalOptions | getAudioTranslationInternalOptions](req *policy.Request, file []byte, options T) error { + body := bytes.Buffer{} + writer := multipart.NewWriter(&body) + + writeContent := func(fieldname, filename string, src io.Reader) error { + fd, err := writer.CreateFormFile(fieldname, filename) + if err != nil { + return err + } + // copy the data to the form file + if _, err = io.Copy(fd, src); err != nil { + return err + } + return nil + } + + if err := writeContent("file", "audio.mp3", bytes.NewReader(file)); err != nil { + return err + } + + switch v := any(options).(type) { + case getAudioTranslationInternalOptions: + if err := writeField(writer, "model", v.Model); err != nil { + return err + } + if err := writeField(writer, "prompt", v.Prompt); err != nil { + return err + } + if err := writeField(writer, "response_format", v.ResponseFormat); err != nil { + return err + } + if err := writeField(writer, "temperature", v.Temperature); err != nil { + return err + } + case getAudioTranscriptionInternalOptions: + if err := writeField(writer, "language", v.Language); err != nil { + return err + } + if err := writeField(writer, "model", v.Model); err != nil { + return err + } + if err := writeField(writer, "prompt", v.Prompt); err != nil { + return err + } + if err := writeField(writer, "response_format", v.ResponseFormat); err != nil { + return err + } + if err := writeField(writer, "temperature", v.Temperature); err != nil { + return err + } + default: + return fmt.Errorf("failed to serialize multipart for unhandled type %T", body) + } + + if err := writer.Close(); err != nil { + return err + } + + return req.SetBody(streaming.NopCloser(bytes.NewReader(body.Bytes())), writer.FormDataContentType()) +} + +func getAudioTranscriptionInternalHandleResponse(resp *http.Response) (getAudioTranscriptionInternalResponse, error) { + at, err := deserializeAudioTranscription(resp) + + if err != nil { + return getAudioTranscriptionInternalResponse{}, err + } + + return getAudioTranscriptionInternalResponse{AudioTranscription: at}, nil +} + +func getAudioTranslationInternalHandleResponse(resp *http.Response) (getAudioTranslationInternalResponse, error) { + at, err := deserializeAudioTranscription(resp) + + if err != nil { + return getAudioTranslationInternalResponse{}, err + } + + return getAudioTranslationInternalResponse{AudioTranscription: at}, nil +} + +// deserializeAudioTranscription handles deserializing the content if it's text/plain +// or a JSON object. +func deserializeAudioTranscription(resp *http.Response) (AudioTranscription, error) { + defer func() { + _ = resp.Request.Body.Close() + }() + + contentType := resp.Header.Get("Content-type") + + if strings.Contains(contentType, "text/plain") { + body, err := io.ReadAll(resp.Body) + + if err != nil { + return AudioTranscription{}, err + } + + return AudioTranscription{ + Text: to.Ptr(string(body)), + }, nil + } + + var result *AudioTranscription + if err := runtime.UnmarshalAsJSON(resp, &result); err != nil { + return AudioTranscription{}, err + } + + return *result, nil +} + +func writeField[T interface { + string | float32 | AudioTranscriptionFormat | AudioTranslationFormat +}](writer *multipart.Writer, fieldName string, v *T) error { + if v == nil { + return nil + } + + switch v2 := any(v).(type) { + case *string: + return writer.WriteField(fieldName, *v2) + case *float32: + return writer.WriteField(fieldName, fmt.Sprintf("%f", *v2)) + case *AudioTranscriptionFormat: + return writer.WriteField(fieldName, string(*v2)) + case *AudioTranslationFormat: + return writer.WriteField(fieldName, string(*v2)) + default: + return fmt.Errorf("no handler for type %T", v) + } +} diff --git a/sdk/ai/azopenai/custom_client_image_test.go b/sdk/ai/azopenai/custom_client_image_test.go index 8f9a34390290..3de3e0f3c1f0 100644 --- a/sdk/ai/azopenai/custom_client_image_test.go +++ b/sdk/ai/azopenai/custom_client_image_test.go @@ -26,12 +26,7 @@ func TestImageGeneration_AzureOpenAI(t *testing.T) { t.Skipf("Ignoring poller-based test") } - cred, err := azopenai.NewKeyCredential(azureOpenAI.APIKey) - require.NoError(t, err) - - client, err := azopenai.NewClientWithKeyCredential(azureOpenAI.Endpoint, cred, newClientOptionsForTest(t)) - require.NoError(t, err) - + client := newTestClient(t, azureOpenAI.Endpoint) testImageGeneration(t, client, azopenai.ImageGenerationResponseFormatURL) } diff --git a/sdk/ai/azopenai/custom_client_test.go b/sdk/ai/azopenai/custom_client_test.go index ce883ce52d8b..f2f338596af4 100644 --- a/sdk/ai/azopenai/custom_client_test.go +++ b/sdk/ai/azopenai/custom_client_test.go @@ -77,12 +77,7 @@ func TestNewClientWithKeyCredential(t *testing.T) { } func TestGetCompletionsStream_AzureOpenAI(t *testing.T) { - cred, err := azopenai.NewKeyCredential(azureOpenAI.APIKey) - require.NoError(t, err) - - client, err := azopenai.NewClientWithKeyCredential(azureOpenAI.Endpoint, cred, newClientOptionsForTest(t)) - require.NoError(t, err) - + client := newTestClient(t, azureOpenAI.Endpoint) testGetCompletionsStream(t, client, azureOpenAI) } @@ -123,10 +118,10 @@ func testGetCompletionsStream(t *testing.T, client *azopenai.Client, tv testVars break } - if completion.PromptAnnotations != nil { + if completion.PromptFilterResults != nil { require.Equal(t, []azopenai.PromptFilterResult{ {PromptIndex: to.Ptr[int32](0), ContentFilterResults: (*azopenai.PromptFilterResultContentFilterResults)(safeContentFilter)}, - }, completion.PromptAnnotations) + }, completion.PromptFilterResults) } eventCount++ diff --git a/sdk/ai/azopenai/custom_models_test.go b/sdk/ai/azopenai/custom_models_test.go index 1438e14998b1..d737e8a206f3 100644 --- a/sdk/ai/azopenai/custom_models_test.go +++ b/sdk/ai/azopenai/custom_models_test.go @@ -56,11 +56,11 @@ func TestParseResponseError(t *testing.T) { contentFilterResults := contentFilterErr.ContentFilterResults // this comment was considered violent, so it was filtered. - require.Equal(t, &ContentFilterResultsViolence{ + require.Equal(t, &ContentFilterResult{ Filtered: to.Ptr(true), Severity: to.Ptr(ContentFilterSeverityMedium)}, contentFilterResults.Violence) - require.Equal(t, &ContentFilterResultsHate{Filtered: to.Ptr(false), Severity: to.Ptr(ContentFilterSeveritySafe)}, contentFilterResults.Hate) - require.Equal(t, &ContentFilterResultsSelfHarm{Filtered: to.Ptr(false), Severity: to.Ptr(ContentFilterSeveritySafe)}, contentFilterResults.SelfHarm) - require.Equal(t, &ContentFilterResultsSexual{Filtered: to.Ptr(false), Severity: to.Ptr(ContentFilterSeveritySafe)}, contentFilterResults.Sexual) + require.Equal(t, &ContentFilterResult{Filtered: to.Ptr(false), Severity: to.Ptr(ContentFilterSeveritySafe)}, contentFilterResults.Hate) + require.Equal(t, &ContentFilterResult{Filtered: to.Ptr(false), Severity: to.Ptr(ContentFilterSeveritySafe)}, contentFilterResults.SelfHarm) + require.Equal(t, &ContentFilterResult{Filtered: to.Ptr(false), Severity: to.Ptr(ContentFilterSeveritySafe)}, contentFilterResults.Sexual) } diff --git a/sdk/ai/azopenai/example_client_audio_test.go b/sdk/ai/azopenai/example_client_audio_test.go new file mode 100644 index 000000000000..9e6495814de7 --- /dev/null +++ b/sdk/ai/azopenai/example_client_audio_test.go @@ -0,0 +1,71 @@ +//go:build go1.18 +// +build go1.18 + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +package azopenai_test + +import ( + "context" + "fmt" + "log" + "os" + + "github.com/Azure/azure-sdk-for-go/sdk/ai/azopenai" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" +) + +func ExampleClient_GetAudioTranscription() { + azureOpenAIKey := os.Getenv("AOAI_API_KEY_WHISPER") + + // Ex: "https://.openai.azure.com" + azureOpenAIEndpoint := os.Getenv("AOAI_ENDPOINT_WHISPER") + + modelDeploymentID := os.Getenv("AOAI_MODEL_WHISPER") + + if azureOpenAIKey == "" || azureOpenAIEndpoint == "" || modelDeploymentID == "" { + fmt.Fprintf(os.Stderr, "Skipping example, environment variables missing\n") + return + } + + keyCredential, err := azopenai.NewKeyCredential(azureOpenAIKey) + + if err != nil { + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) + } + + client, err := azopenai.NewClientWithKeyCredential(azureOpenAIEndpoint, keyCredential, nil) + + if err != nil { + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) + } + + mp3Bytes, err := os.ReadFile("testdata/sampledata_audiofiles_myVoiceIsMyPassportVerifyMe01.mp3") + + if err != nil { + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) + } + + resp, err := client.GetAudioTranscription(context.TODO(), azopenai.AudioTranscriptionOptions{ + File: mp3Bytes, + + // this will return _just_ the translated text. Other formats are available, which return + // different or additional metadata. See [azopenai.AudioTranscriptionFormat] for more examples. + ResponseFormat: to.Ptr(azopenai.AudioTranscriptionFormatText), + + Deployment: modelDeploymentID, + }, nil) + + if err != nil { + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) + } + + fmt.Fprintf(os.Stderr, "Transcribed text: %s\n", *resp.Text) + + // Output: +} diff --git a/sdk/ai/azopenai/example_client_embeddings_test.go b/sdk/ai/azopenai/example_client_embeddings_test.go index 6080dc366097..4074e714fe35 100644 --- a/sdk/ai/azopenai/example_client_embeddings_test.go +++ b/sdk/ai/azopenai/example_client_embeddings_test.go @@ -14,7 +14,7 @@ import ( func ExampleClient_GetEmbeddings() { azureOpenAIKey := os.Getenv("AOAI_API_KEY") - modelDeploymentID := os.Getenv("AOAI_EMBEDDINGS_MODEL_DEPLOYMENT") + modelDeploymentID := os.Getenv("AOAI_EMBEDDINGS_MODEL") // Ex: "https://.openai.azure.com" azureOpenAIEndpoint := os.Getenv("AOAI_ENDPOINT") diff --git a/sdk/ai/azopenai/example_client_getchatcompletions_extensions_test.go b/sdk/ai/azopenai/example_client_getchatcompletions_extensions_test.go index fc027cdf7760..8cd7dca7b072 100644 --- a/sdk/ai/azopenai/example_client_getchatcompletions_extensions_test.go +++ b/sdk/ai/azopenai/example_client_getchatcompletions_extensions_test.go @@ -19,7 +19,7 @@ import ( // [Azure OpenAI on your data]: https://learn.microsoft.com/azure/ai-services/openai/concepts/use-your-data func ExampleClient_GetChatCompletions_bringYourOwnDataWithCognitiveSearch() { azureOpenAIKey := os.Getenv("AOAI_API_KEY") - modelDeploymentID := os.Getenv("AOAI_CHAT_COMPLETIONS_MODEL_DEPLOYMENT") + modelDeploymentID := os.Getenv("AOAI_CHAT_COMPLETIONS_MODEL") // Ex: "https://.openai.azure.com" azureOpenAIEndpoint := os.Getenv("AOAI_ENDPOINT") diff --git a/sdk/ai/azopenai/example_client_getchatcompletions_test.go b/sdk/ai/azopenai/example_client_getchatcompletions_test.go index e53cbb847938..879552bf122a 100644 --- a/sdk/ai/azopenai/example_client_getchatcompletions_test.go +++ b/sdk/ai/azopenai/example_client_getchatcompletions_test.go @@ -18,7 +18,7 @@ import ( func ExampleClient_GetChatCompletions() { azureOpenAIKey := os.Getenv("AOAI_API_KEY") - modelDeploymentID := os.Getenv("AOAI_CHAT_COMPLETIONS_MODEL_DEPLOYMENT") + modelDeploymentID := os.Getenv("AOAI_CHAT_COMPLETIONS_MODEL") // Ex: "https://.openai.azure.com" azureOpenAIEndpoint := os.Getenv("AOAI_ENDPOINT") @@ -90,7 +90,7 @@ func ExampleClient_GetChatCompletions() { func ExampleClient_GetChatCompletions_functions() { azureOpenAIKey := os.Getenv("AOAI_API_KEY") - modelDeploymentID := os.Getenv("AOAI_CHAT_COMPLETIONS_MODEL_DEPLOYMENT") + modelDeploymentID := os.Getenv("AOAI_CHAT_COMPLETIONS_MODEL") // Ex: "https://.openai.azure.com" azureOpenAIEndpoint := os.Getenv("AOAI_ENDPOINT") @@ -183,7 +183,7 @@ func ExampleClient_GetChatCompletions_functions() { func ExampleClient_GetChatCompletionsStream() { azureOpenAIKey := os.Getenv("AOAI_API_KEY") - modelDeploymentID := os.Getenv("AOAI_CHAT_COMPLETIONS_MODEL_DEPLOYMENT") + modelDeploymentID := os.Getenv("AOAI_CHAT_COMPLETIONS_MODEL") // Ex: "https://.openai.azure.com" azureOpenAIEndpoint := os.Getenv("AOAI_ENDPOINT") diff --git a/sdk/ai/azopenai/example_client_getcompletions_test.go b/sdk/ai/azopenai/example_client_getcompletions_test.go index d8a06131ecc8..31a7be77ed3b 100644 --- a/sdk/ai/azopenai/example_client_getcompletions_test.go +++ b/sdk/ai/azopenai/example_client_getcompletions_test.go @@ -17,7 +17,7 @@ import ( func ExampleClient_GetCompletions() { azureOpenAIKey := os.Getenv("AOAI_API_KEY") - modelDeployment := os.Getenv("AOAI_COMPLETIONS_MODEL_DEPLOYMENT") + modelDeployment := os.Getenv("AOAI_COMPLETIONS_MODEL") // Ex: "https://.openai.azure.com" azureOpenAIEndpoint := os.Getenv("AOAI_ENDPOINT") @@ -64,7 +64,7 @@ func ExampleClient_GetCompletions() { func ExampleClient_GetCompletionsStream() { azureOpenAIKey := os.Getenv("AOAI_API_KEY") - modelDeploymentID := os.Getenv("AOAI_COMPLETIONS_MODEL_DEPLOYMENT") + modelDeploymentID := os.Getenv("AOAI_COMPLETIONS_MODEL") // Ex: "https://.openai.azure.com" azureOpenAIEndpoint := os.Getenv("AOAI_ENDPOINT") diff --git a/sdk/ai/azopenai/models.go b/sdk/ai/azopenai/models.go index 6fc762a7124c..2c1557e73150 100644 --- a/sdk/ai/azopenai/models.go +++ b/sdk/ai/azopenai/models.go @@ -10,6 +10,169 @@ package azopenai import "time" +// AudioTranscription - Result information for an operation that transcribed spoken audio into written text. +type AudioTranscription struct { + // REQUIRED; The transcribed text for the provided audio data. + Text *string + + // The total duration of the audio processed to produce accompanying transcription information. + Duration *float32 + + // The spoken language that was detected in the transcribed audio data. This is expressed as a two-letter ISO-639-1 language + // code like 'en' or 'fr'. + Language *string + + // A collection of information about the timing, probabilities, and other detail of each processed audio segment. + Segments []AudioTranscriptionSegment + + // The label that describes which operation type generated the accompanying response data. + Task *AudioTaskLabel +} + +// AudioTranscriptionOptions - The configuration information for an audio transcription request. +type AudioTranscriptionOptions struct { + // REQUIRED; The audio data to transcribe. This must be the binary content of a file in one of the supported media formats: + // flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, webm. + File []byte + + // The primary spoken language of the audio data to be transcribed, supplied as a two-letter ISO-639-1 language code such + // as 'en' or 'fr'. Providing this known input language is optional but may improve + // the accuracy and/or latency of transcription. + Language *string + + // REQUIRED: Deployment specifies the name of the deployment (for Azure OpenAI) or model (for OpenAI) to use for this request. + Deployment string + + // An optional hint to guide the model's style or continue from a prior audio segment. The written language of the prompt + // should match the primary spoken language of the audio data. + Prompt *string + + // The requested format of the transcription response data, which will influence the content and detail of the result. + ResponseFormat *AudioTranscriptionFormat + + // The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values + // like 0.2 will make it more focused and deterministic. If set to 0, the model will + // use log probability to automatically increase the temperature until certain thresholds are hit. + Temperature *float32 +} + +// AudioTranscriptionSegment - Extended information about a single segment of transcribed audio data. Segments generally represent +// roughly 5-10 seconds of speech. Segment boundaries typically occur between words but not necessarily +// sentences. +type AudioTranscriptionSegment struct { + // REQUIRED; The average log probability associated with this audio segment. + AvgLogprob *float32 + + // REQUIRED; The compression ratio of this audio segment. + CompressionRatio *float32 + + // REQUIRED; The time at which this segment ended relative to the beginning of the transcribed audio. + End *float32 + + // REQUIRED; The 0-based index of this segment within a transcription. + ID *int32 + + // REQUIRED; The probability of no speech detection within this audio segment. + NoSpeechProb *float32 + + // REQUIRED; The seek position associated with the processing of this audio segment. Seek positions are expressed as hundredths + // of seconds. The model may process several segments from a single seek position, so + // while the seek position will never represent a later time than the segment's start, the segment's start may represent a + // significantly later time than the segment's associated seek position. + Seek *int32 + + // REQUIRED; The time at which this segment started relative to the beginning of the transcribed audio. + Start *float32 + + // REQUIRED; The temperature score associated with this audio segment. + Temperature *float32 + + // REQUIRED; The transcribed text that was part of this audio segment. + Text *string + + // REQUIRED; The token IDs matching the transcribed text in this audio segment. + Tokens []int32 +} + +// AudioTranslation - Result information for an operation that translated spoken audio into written text. +type AudioTranslation struct { + // REQUIRED; The translated text for the provided audio data. + Text *string + + // The total duration of the audio processed to produce accompanying translation information. + Duration *float32 + + // The spoken language that was detected in the translated audio data. This is expressed as a two-letter ISO-639-1 language + // code like 'en' or 'fr'. + Language *string + + // A collection of information about the timing, probabilities, and other detail of each processed audio segment. + Segments []AudioTranslationSegment + + // The label that describes which operation type generated the accompanying response data. + Task *AudioTaskLabel +} + +// AudioTranslationOptions - The configuration information for an audio translation request. +type AudioTranslationOptions struct { + // REQUIRED; The audio data to translate. This must be the binary content of a file in one of the supported media formats: + // flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, webm. + File []byte + + // REQUIRED: Deployment specifies the name of the deployment (for Azure OpenAI) or model (for OpenAI) to use for this request. + Deployment string + + // An optional hint to guide the model's style or continue from a prior audio segment. The written language of the prompt + // should match the primary spoken language of the audio data. + Prompt *string + + // The requested format of the translation response data, which will influence the content and detail of the result. + ResponseFormat *AudioTranslationFormat + + // The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values + // like 0.2 will make it more focused and deterministic. If set to 0, the model will + // use log probability to automatically increase the temperature until certain thresholds are hit. + Temperature *float32 +} + +// AudioTranslationSegment - Extended information about a single segment of translated audio data. Segments generally represent +// roughly 5-10 seconds of speech. Segment boundaries typically occur between words but not necessarily +// sentences. +type AudioTranslationSegment struct { + // REQUIRED; The average log probability associated with this audio segment. + AvgLogprob *float32 + + // REQUIRED; The compression ratio of this audio segment. + CompressionRatio *float32 + + // REQUIRED; The time at which this segment ended relative to the beginning of the translated audio. + End *float32 + + // REQUIRED; The 0-based index of this segment within a translation. + ID *int32 + + // REQUIRED; The probability of no speech detection within this audio segment. + NoSpeechProb *float32 + + // REQUIRED; The seek position associated with the processing of this audio segment. Seek positions are expressed as hundredths + // of seconds. The model may process several segments from a single seek position, so + // while the seek position will never represent a later time than the segment's start, the segment's start may represent a + // significantly later time than the segment's associated seek position. + Seek *int32 + + // REQUIRED; The time at which this segment started relative to the beginning of the translated audio. + Start *float32 + + // REQUIRED; The temperature score associated with this audio segment. + Temperature *float32 + + // REQUIRED; The translated text that was part of this audio segment. + Text *string + + // REQUIRED; The token IDs matching the translated text in this audio segment. + Tokens []int32 +} + // AzureChatExtensionConfiguration - A representation of configuration data for a single Azure OpenAI chat extension. This // will be used by a chat completions request that should use Azure OpenAI chat extensions to augment the response // behavior. The use of this configuration is compatible only with Azure OpenAI. @@ -117,8 +280,8 @@ type AzureCognitiveSearchIndexFieldMappingOptions struct { VectorFields []string } -// azureCoreFoundationsError - The error object. -type azureCoreFoundationsError struct { +// Error - The error object. +type Error struct { // REQUIRED; One of a server-defined set of error codes. Code *string @@ -126,33 +289,33 @@ type azureCoreFoundationsError struct { Message *string // An array of details about specific errors that led to this reported error. - Details []azureCoreFoundationsError + Details []Error // An object containing more specific information than the current object about the error. - Innererror *azureCoreFoundationsErrorInnererror + InnerError *InnerError // The target of the error. Target *string } -// azureCoreFoundationsErrorInnererror - An object containing more specific information than the current object about the +// ErrorInnererror - An object containing more specific information than the current object about the // error. -type azureCoreFoundationsErrorInnererror struct { +type ErrorInnererror struct { // One of a server-defined set of error codes. Code *string // Inner error. - Innererror *azureCoreFoundationsInnerErrorInnererror + InnerError *InnerError } -// azureCoreFoundationsErrorResponse - A response containing error details. -type azureCoreFoundationsErrorResponse struct { +// ErrorResponse - A response containing error details. +type ErrorResponse struct { // REQUIRED; The error object. - Error *azureCoreFoundationsErrorResponseError + Error *ErrorResponseError } -// azureCoreFoundationsErrorResponseError - The error object. -type azureCoreFoundationsErrorResponseError struct { +// ErrorResponseError - The error object. +type ErrorResponseError struct { // REQUIRED; One of a server-defined set of error codes. Code *string @@ -160,33 +323,24 @@ type azureCoreFoundationsErrorResponseError struct { Message *string // An array of details about specific errors that led to this reported error. - Details []azureCoreFoundationsError + Details []Error // An object containing more specific information than the current object about the error. - Innererror *azureCoreFoundationsErrorInnererror + InnerError *InnerError // The target of the error. Target *string } -// azureCoreFoundationsInnerError - An object containing more specific information about the error. As per Microsoft One API +// InnerError - An object containing more specific information about the error. As per Microsoft One API // guidelines - // https://github.com/Microsoft/api-guidelines/blob/vNext/Guidelines.md#7102-error-condition-responses. -type azureCoreFoundationsInnerError struct { +type InnerError struct { // One of a server-defined set of error codes. Code *string // Inner error. - Innererror *azureCoreFoundationsInnerErrorInnererror -} - -// azureCoreFoundationsInnerErrorInnererror - Inner error. -type azureCoreFoundationsInnerErrorInnererror struct { - // One of a server-defined set of error codes. - Code *string - - // Inner error. - Innererror *azureCoreFoundationsInnerErrorInnererror + InnerError *InnerError } // batchImageGenerationOperationResponse - A polling status update or final response payload for an image operation. @@ -201,7 +355,7 @@ type batchImageGenerationOperationResponse struct { Status *azureOpenAIOperationState // The error if the operation failed. - Error *azureCoreFoundationsError + Error *Error // A timestamp when this operation and its associated images expire and will be deleted (in unix epochs). Expires *int64 @@ -236,23 +390,26 @@ type ChatChoice struct { // it has been detected, as well as the severity level (verylow, low, medium, high-scale that determines the // intensity and risk level of harmful content) and if it has been filtered or not. type ChatChoiceContentFilterResults struct { + // Describes an error returned if the content filtering system is down or otherwise unable to complete the operation in time. + Error *ContentFilterResultsError + // Describes language attacks or uses that include pejorative or discriminatory language with reference to a person or identity // group on the basis of certain differentiating attributes of these groups // including but not limited to race, ethnicity, nationality, gender identity and expression, sexual orientation, religion, // immigration status, ability status, personal appearance, and body size. - Hate *ContentFilterResultsHate + Hate *ContentFilterResult // Describes language related to physical actions intended to purposely hurt, injure, or damage one’s body, or kill oneself. - SelfHarm *ContentFilterResultsSelfHarm + SelfHarm *ContentFilterResult // Describes language related to anatomical organs and genitals, romantic relationships, acts portrayed in erotic or affectionate // terms, physical sexual acts, including those portrayed as an assault or a // forced sexual violent act against one’s will, prostitution, pornography, and abuse. - Sexual *ContentFilterResultsSexual + Sexual *ContentFilterResult // Describes language related to physical actions intended to hurt, injure, damage, or kill someone or something; describes // weapons, etc. - Violence *ContentFilterResultsViolence + Violence *ContentFilterResult } // ChatChoiceDelta - The delta message content for a streaming response. @@ -321,7 +478,7 @@ type ChatCompletions struct { // Content filtering results for zero or more prompts in the request. In a streaming request, results for different prompts // may arrive at different times or in different orders. - PromptAnnotations []PromptFilterResult + PromptFilterResults []PromptFilterResult } // ChatCompletionsOptions - The configuration information for a chat completions request. Completions support a wide variety @@ -466,23 +623,26 @@ type Choice struct { // has been detected, as well as the severity level (verylow, low, medium, high-scale that determines the // intensity and risk level of harmful content) and if it has been filtered or not. type ChoiceContentFilterResults struct { + // Describes an error returned if the content filtering system is down or otherwise unable to complete the operation in time. + Error *ContentFilterResultsError + // Describes language attacks or uses that include pejorative or discriminatory language with reference to a person or identity // group on the basis of certain differentiating attributes of these groups // including but not limited to race, ethnicity, nationality, gender identity and expression, sexual orientation, religion, // immigration status, ability status, personal appearance, and body size. - Hate *ContentFilterResultsHate + Hate *ContentFilterResult // Describes language related to physical actions intended to purposely hurt, injure, or damage one’s body, or kill oneself. - SelfHarm *ContentFilterResultsSelfHarm + SelfHarm *ContentFilterResult // Describes language related to anatomical organs and genitals, romantic relationships, acts portrayed in erotic or affectionate // terms, physical sexual acts, including those portrayed as an assault or a // forced sexual violent act against one’s will, prostitution, pornography, and abuse. - Sexual *ContentFilterResultsSexual + Sexual *ContentFilterResult // Describes language related to physical actions intended to hurt, injure, damage, or kill someone or something; describes // weapons, etc. - Violence *ContentFilterResultsViolence + Violence *ContentFilterResult } // ChoiceLogProbs - The log probabilities model for tokens associated with this completions choice. @@ -497,7 +657,7 @@ type ChoiceLogProbs struct { Tokens []string // REQUIRED; A mapping of tokens to maximum log probability values in this completions data. - TopLogProbs []any + TopLogProbs []map[string]*float32 } // Completions - Representation of the response data from a completions request. Completions support a wide variety of tasks @@ -520,7 +680,7 @@ type Completions struct { // Content filtering results for zero or more prompts in the request. In a streaming request, results for different prompts // may arrive at different times or in different orders. - PromptAnnotations []PromptFilterResult + PromptFilterResults []PromptFilterResult } // CompletionsLogProbabilityModel - Representation of a log probabilities model for a completions generation. @@ -535,7 +695,7 @@ type CompletionsLogProbabilityModel struct { Tokens []string // REQUIRED; A mapping of tokens to maximum log probability values in this completions data. - TopLogProbs []any + TopLogProbs []map[string]*float32 } // CompletionsOptions - The configuration information for a completions request. Completions support a wide variety of tasks @@ -618,68 +778,56 @@ type CompletionsUsage struct { TotalTokens *int32 } +// ContentFilterResult - Information about filtered content severity level and if it has been filtered or not. +type ContentFilterResult struct { + // REQUIRED; A value indicating whether or not the content has been filtered. + Filtered *bool + + // REQUIRED; Ratings for the intensity and risk level of filtered content. + Severity *ContentFilterSeverity +} + // ContentFilterResults - Information about the content filtering category, if it has been detected. type ContentFilterResults struct { + // Describes an error returned if the content filtering system is down or otherwise unable to complete the operation in time. + Error *ContentFilterResultsError + // Describes language attacks or uses that include pejorative or discriminatory language with reference to a person or identity // group on the basis of certain differentiating attributes of these groups // including but not limited to race, ethnicity, nationality, gender identity and expression, sexual orientation, religion, // immigration status, ability status, personal appearance, and body size. - Hate *ContentFilterResultsHate + Hate *ContentFilterResult // Describes language related to physical actions intended to purposely hurt, injure, or damage one’s body, or kill oneself. - SelfHarm *ContentFilterResultsSelfHarm + SelfHarm *ContentFilterResult // Describes language related to anatomical organs and genitals, romantic relationships, acts portrayed in erotic or affectionate // terms, physical sexual acts, including those portrayed as an assault or a // forced sexual violent act against one’s will, prostitution, pornography, and abuse. - Sexual *ContentFilterResultsSexual + Sexual *ContentFilterResult // Describes language related to physical actions intended to hurt, injure, damage, or kill someone or something; describes // weapons, etc. - Violence *ContentFilterResultsViolence -} - -// ContentFilterResultsHate - Describes language attacks or uses that include pejorative or discriminatory language with reference -// to a person or identity group on the basis of certain differentiating attributes of these groups -// including but not limited to race, ethnicity, nationality, gender identity and expression, sexual orientation, religion, -// immigration status, ability status, personal appearance, and body size. -type ContentFilterResultsHate struct { - // REQUIRED; A value indicating whether or not the content has been filtered. - Filtered *bool - - // REQUIRED; Ratings for the intensity and risk level of filtered content. - Severity *ContentFilterSeverity + Violence *ContentFilterResult } -// ContentFilterResultsSelfHarm - Describes language related to physical actions intended to purposely hurt, injure, or damage -// one’s body, or kill oneself. -type ContentFilterResultsSelfHarm struct { - // REQUIRED; A value indicating whether or not the content has been filtered. - Filtered *bool - - // REQUIRED; Ratings for the intensity and risk level of filtered content. - Severity *ContentFilterSeverity -} +// ContentFilterResultsError - Describes an error returned if the content filtering system is down or otherwise unable to +// complete the operation in time. +type ContentFilterResultsError struct { + // REQUIRED; One of a server-defined set of error codes. + Code *string -// ContentFilterResultsSexual - Describes language related to anatomical organs and genitals, romantic relationships, acts -// portrayed in erotic or affectionate terms, physical sexual acts, including those portrayed as an assault or a -// forced sexual violent act against one’s will, prostitution, pornography, and abuse. -type ContentFilterResultsSexual struct { - // REQUIRED; A value indicating whether or not the content has been filtered. - Filtered *bool + // REQUIRED; A human-readable representation of the error. + Message *string - // REQUIRED; Ratings for the intensity and risk level of filtered content. - Severity *ContentFilterSeverity -} + // An array of details about specific errors that led to this reported error. + Details []Error -// ContentFilterResultsViolence - Describes language related to physical actions intended to hurt, injure, damage, or kill -// someone or something; describes weapons, etc. -type ContentFilterResultsViolence struct { - // REQUIRED; A value indicating whether or not the content has been filtered. - Filtered *bool + // An object containing more specific information than the current object about the error. + InnerError *InnerError - // REQUIRED; Ratings for the intensity and risk level of filtered content. - Severity *ContentFilterSeverity + // The target of the error. + Target *string } // Deployment - A specific deployment @@ -826,21 +974,24 @@ type PromptFilterResult struct { // PromptFilterResultContentFilterResults - Content filtering results for this prompt type PromptFilterResultContentFilterResults struct { + // Describes an error returned if the content filtering system is down or otherwise unable to complete the operation in time. + Error *ContentFilterResultsError + // Describes language attacks or uses that include pejorative or discriminatory language with reference to a person or identity // group on the basis of certain differentiating attributes of these groups // including but not limited to race, ethnicity, nationality, gender identity and expression, sexual orientation, religion, // immigration status, ability status, personal appearance, and body size. - Hate *ContentFilterResultsHate + Hate *ContentFilterResult // Describes language related to physical actions intended to purposely hurt, injure, or damage one’s body, or kill oneself. - SelfHarm *ContentFilterResultsSelfHarm + SelfHarm *ContentFilterResult // Describes language related to anatomical organs and genitals, romantic relationships, acts portrayed in erotic or affectionate // terms, physical sexual acts, including those portrayed as an assault or a // forced sexual violent act against one’s will, prostitution, pornography, and abuse. - Sexual *ContentFilterResultsSexual + Sexual *ContentFilterResult // Describes language related to physical actions intended to hurt, injure, damage, or kill someone or something; describes // weapons, etc. - Violence *ContentFilterResultsViolence + Violence *ContentFilterResult } diff --git a/sdk/ai/azopenai/models_serde.go b/sdk/ai/azopenai/models_serde.go index 6c3cab72cd2f..8fad187ff4f4 100644 --- a/sdk/ai/azopenai/models_serde.go +++ b/sdk/ai/azopenai/models_serde.go @@ -14,8 +14,311 @@ import ( "reflect" "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" ) +// MarshalJSON implements the json.Marshaller interface for type AudioTranscription. +func (a AudioTranscription) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "duration", a.Duration) + populate(objectMap, "language", a.Language) + populate(objectMap, "segments", a.Segments) + populate(objectMap, "task", a.Task) + populate(objectMap, "text", a.Text) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type AudioTranscription. +func (a *AudioTranscription) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "duration": + err = unpopulate(val, "Duration", &a.Duration) + delete(rawMsg, key) + case "language": + err = unpopulate(val, "Language", &a.Language) + delete(rawMsg, key) + case "segments": + err = unpopulate(val, "Segments", &a.Segments) + delete(rawMsg, key) + case "task": + err = unpopulate(val, "Task", &a.Task) + delete(rawMsg, key) + case "text": + err = unpopulate(val, "Text", &a.Text) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type AudioTranscriptionOptions. +func (a AudioTranscriptionOptions) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populateByteArray(objectMap, "file", a.File, runtime.Base64StdFormat) + populate(objectMap, "language", a.Language) + populate(objectMap, "model", &a.Deployment) + populate(objectMap, "prompt", a.Prompt) + populate(objectMap, "response_format", a.ResponseFormat) + populate(objectMap, "temperature", a.Temperature) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type AudioTranscriptionOptions. +func (a *AudioTranscriptionOptions) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "file": + err = runtime.DecodeByteArray(string(val), &a.File, runtime.Base64StdFormat) + delete(rawMsg, key) + case "language": + err = unpopulate(val, "Language", &a.Language) + delete(rawMsg, key) + case "model": + err = unpopulate(val, "Model", &a.Deployment) + delete(rawMsg, key) + case "prompt": + err = unpopulate(val, "Prompt", &a.Prompt) + delete(rawMsg, key) + case "response_format": + err = unpopulate(val, "ResponseFormat", &a.ResponseFormat) + delete(rawMsg, key) + case "temperature": + err = unpopulate(val, "Temperature", &a.Temperature) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type AudioTranscriptionSegment. +func (a AudioTranscriptionSegment) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "avg_logprob", a.AvgLogprob) + populate(objectMap, "compression_ratio", a.CompressionRatio) + populate(objectMap, "end", a.End) + populate(objectMap, "id", a.ID) + populate(objectMap, "no_speech_prob", a.NoSpeechProb) + populate(objectMap, "seek", a.Seek) + populate(objectMap, "start", a.Start) + populate(objectMap, "temperature", a.Temperature) + populate(objectMap, "text", a.Text) + populate(objectMap, "tokens", a.Tokens) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type AudioTranscriptionSegment. +func (a *AudioTranscriptionSegment) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "avg_logprob": + err = unpopulate(val, "AvgLogprob", &a.AvgLogprob) + delete(rawMsg, key) + case "compression_ratio": + err = unpopulate(val, "CompressionRatio", &a.CompressionRatio) + delete(rawMsg, key) + case "end": + err = unpopulate(val, "End", &a.End) + delete(rawMsg, key) + case "id": + err = unpopulate(val, "ID", &a.ID) + delete(rawMsg, key) + case "no_speech_prob": + err = unpopulate(val, "NoSpeechProb", &a.NoSpeechProb) + delete(rawMsg, key) + case "seek": + err = unpopulate(val, "Seek", &a.Seek) + delete(rawMsg, key) + case "start": + err = unpopulate(val, "Start", &a.Start) + delete(rawMsg, key) + case "temperature": + err = unpopulate(val, "Temperature", &a.Temperature) + delete(rawMsg, key) + case "text": + err = unpopulate(val, "Text", &a.Text) + delete(rawMsg, key) + case "tokens": + err = unpopulate(val, "Tokens", &a.Tokens) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type AudioTranslation. +func (a AudioTranslation) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "duration", a.Duration) + populate(objectMap, "language", a.Language) + populate(objectMap, "segments", a.Segments) + populate(objectMap, "task", a.Task) + populate(objectMap, "text", a.Text) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type AudioTranslation. +func (a *AudioTranslation) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "duration": + err = unpopulate(val, "Duration", &a.Duration) + delete(rawMsg, key) + case "language": + err = unpopulate(val, "Language", &a.Language) + delete(rawMsg, key) + case "segments": + err = unpopulate(val, "Segments", &a.Segments) + delete(rawMsg, key) + case "task": + err = unpopulate(val, "Task", &a.Task) + delete(rawMsg, key) + case "text": + err = unpopulate(val, "Text", &a.Text) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type AudioTranslationOptions. +func (a AudioTranslationOptions) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populateByteArray(objectMap, "file", a.File, runtime.Base64StdFormat) + populate(objectMap, "model", &a.Deployment) + populate(objectMap, "prompt", a.Prompt) + populate(objectMap, "response_format", a.ResponseFormat) + populate(objectMap, "temperature", a.Temperature) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type AudioTranslationOptions. +func (a *AudioTranslationOptions) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "file": + err = runtime.DecodeByteArray(string(val), &a.File, runtime.Base64StdFormat) + delete(rawMsg, key) + case "model": + err = unpopulate(val, "Model", &a.Deployment) + delete(rawMsg, key) + case "prompt": + err = unpopulate(val, "Prompt", &a.Prompt) + delete(rawMsg, key) + case "response_format": + err = unpopulate(val, "ResponseFormat", &a.ResponseFormat) + delete(rawMsg, key) + case "temperature": + err = unpopulate(val, "Temperature", &a.Temperature) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type AudioTranslationSegment. +func (a AudioTranslationSegment) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "avg_logprob", a.AvgLogprob) + populate(objectMap, "compression_ratio", a.CompressionRatio) + populate(objectMap, "end", a.End) + populate(objectMap, "id", a.ID) + populate(objectMap, "no_speech_prob", a.NoSpeechProb) + populate(objectMap, "seek", a.Seek) + populate(objectMap, "start", a.Start) + populate(objectMap, "temperature", a.Temperature) + populate(objectMap, "text", a.Text) + populate(objectMap, "tokens", a.Tokens) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type AudioTranslationSegment. +func (a *AudioTranslationSegment) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "avg_logprob": + err = unpopulate(val, "AvgLogprob", &a.AvgLogprob) + delete(rawMsg, key) + case "compression_ratio": + err = unpopulate(val, "CompressionRatio", &a.CompressionRatio) + delete(rawMsg, key) + case "end": + err = unpopulate(val, "End", &a.End) + delete(rawMsg, key) + case "id": + err = unpopulate(val, "ID", &a.ID) + delete(rawMsg, key) + case "no_speech_prob": + err = unpopulate(val, "NoSpeechProb", &a.NoSpeechProb) + delete(rawMsg, key) + case "seek": + err = unpopulate(val, "Seek", &a.Seek) + delete(rawMsg, key) + case "start": + err = unpopulate(val, "Start", &a.Start) + delete(rawMsg, key) + case "temperature": + err = unpopulate(val, "Temperature", &a.Temperature) + delete(rawMsg, key) + case "text": + err = unpopulate(val, "Text", &a.Text) + delete(rawMsg, key) + case "tokens": + err = unpopulate(val, "Tokens", &a.Tokens) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + } + return nil +} + // MarshalJSON implements the json.Marshaller interface for type AzureChatExtensionConfiguration. func (a AzureChatExtensionConfiguration) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) @@ -235,19 +538,19 @@ func (a *AzureCognitiveSearchIndexFieldMappingOptions) UnmarshalJSON(data []byte return nil } -// MarshalJSON implements the json.Marshaller interface for type azureCoreFoundationsError. -func (a azureCoreFoundationsError) MarshalJSON() ([]byte, error) { +// MarshalJSON implements the json.Marshaller interface for type Error. +func (a Error) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) populate(objectMap, "code", a.Code) populate(objectMap, "details", a.Details) - populate(objectMap, "innererror", a.Innererror) + populate(objectMap, "innererror", a.InnerError) populate(objectMap, "message", a.Message) populate(objectMap, "target", a.Target) return json.Marshal(objectMap) } -// UnmarshalJSON implements the json.Unmarshaller interface for type azureCoreFoundationsError. -func (a *azureCoreFoundationsError) UnmarshalJSON(data []byte) error { +// UnmarshalJSON implements the json.Unmarshaller interface for type Error. +func (a *Error) UnmarshalJSON(data []byte) error { var rawMsg map[string]json.RawMessage if err := json.Unmarshal(data, &rawMsg); err != nil { return fmt.Errorf("unmarshalling type %T: %v", a, err) @@ -262,7 +565,7 @@ func (a *azureCoreFoundationsError) UnmarshalJSON(data []byte) error { err = unpopulate(val, "Details", &a.Details) delete(rawMsg, key) case "innererror": - err = unpopulate(val, "Innererror", &a.Innererror) + err = unpopulate(val, "Innererror", &a.InnerError) delete(rawMsg, key) case "message": err = unpopulate(val, "Message", &a.Message) @@ -278,16 +581,16 @@ func (a *azureCoreFoundationsError) UnmarshalJSON(data []byte) error { return nil } -// MarshalJSON implements the json.Marshaller interface for type azureCoreFoundationsErrorInnererror. -func (a azureCoreFoundationsErrorInnererror) MarshalJSON() ([]byte, error) { +// MarshalJSON implements the json.Marshaller interface for type ErrorInnererror. +func (a ErrorInnererror) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) populate(objectMap, "code", a.Code) - populate(objectMap, "innererror", a.Innererror) + populate(objectMap, "innererror", a.InnerError) return json.Marshal(objectMap) } -// UnmarshalJSON implements the json.Unmarshaller interface for type azureCoreFoundationsErrorInnererror. -func (a *azureCoreFoundationsErrorInnererror) UnmarshalJSON(data []byte) error { +// UnmarshalJSON implements the json.Unmarshaller interface for type ErrorInnererror. +func (a *ErrorInnererror) UnmarshalJSON(data []byte) error { var rawMsg map[string]json.RawMessage if err := json.Unmarshal(data, &rawMsg); err != nil { return fmt.Errorf("unmarshalling type %T: %v", a, err) @@ -299,7 +602,7 @@ func (a *azureCoreFoundationsErrorInnererror) UnmarshalJSON(data []byte) error { err = unpopulate(val, "Code", &a.Code) delete(rawMsg, key) case "innererror": - err = unpopulate(val, "Innererror", &a.Innererror) + err = unpopulate(val, "Innererror", &a.InnerError) delete(rawMsg, key) } if err != nil { @@ -309,15 +612,15 @@ func (a *azureCoreFoundationsErrorInnererror) UnmarshalJSON(data []byte) error { return nil } -// MarshalJSON implements the json.Marshaller interface for type azureCoreFoundationsErrorResponse. -func (a azureCoreFoundationsErrorResponse) MarshalJSON() ([]byte, error) { +// MarshalJSON implements the json.Marshaller interface for type ErrorResponse. +func (a ErrorResponse) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) populate(objectMap, "error", a.Error) return json.Marshal(objectMap) } -// UnmarshalJSON implements the json.Unmarshaller interface for type azureCoreFoundationsErrorResponse. -func (a *azureCoreFoundationsErrorResponse) UnmarshalJSON(data []byte) error { +// UnmarshalJSON implements the json.Unmarshaller interface for type ErrorResponse. +func (a *ErrorResponse) UnmarshalJSON(data []byte) error { var rawMsg map[string]json.RawMessage if err := json.Unmarshal(data, &rawMsg); err != nil { return fmt.Errorf("unmarshalling type %T: %v", a, err) @@ -336,19 +639,19 @@ func (a *azureCoreFoundationsErrorResponse) UnmarshalJSON(data []byte) error { return nil } -// MarshalJSON implements the json.Marshaller interface for type azureCoreFoundationsErrorResponseError. -func (a azureCoreFoundationsErrorResponseError) MarshalJSON() ([]byte, error) { +// MarshalJSON implements the json.Marshaller interface for type ErrorResponseError. +func (a ErrorResponseError) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) populate(objectMap, "code", a.Code) populate(objectMap, "details", a.Details) - populate(objectMap, "innererror", a.Innererror) + populate(objectMap, "innererror", a.InnerError) populate(objectMap, "message", a.Message) populate(objectMap, "target", a.Target) return json.Marshal(objectMap) } -// UnmarshalJSON implements the json.Unmarshaller interface for type azureCoreFoundationsErrorResponseError. -func (a *azureCoreFoundationsErrorResponseError) UnmarshalJSON(data []byte) error { +// UnmarshalJSON implements the json.Unmarshaller interface for type ErrorResponseError. +func (a *ErrorResponseError) UnmarshalJSON(data []byte) error { var rawMsg map[string]json.RawMessage if err := json.Unmarshal(data, &rawMsg); err != nil { return fmt.Errorf("unmarshalling type %T: %v", a, err) @@ -363,7 +666,7 @@ func (a *azureCoreFoundationsErrorResponseError) UnmarshalJSON(data []byte) erro err = unpopulate(val, "Details", &a.Details) delete(rawMsg, key) case "innererror": - err = unpopulate(val, "Innererror", &a.Innererror) + err = unpopulate(val, "Innererror", &a.InnerError) delete(rawMsg, key) case "message": err = unpopulate(val, "Message", &a.Message) @@ -379,16 +682,16 @@ func (a *azureCoreFoundationsErrorResponseError) UnmarshalJSON(data []byte) erro return nil } -// MarshalJSON implements the json.Marshaller interface for type azureCoreFoundationsInnerError. -func (a azureCoreFoundationsInnerError) MarshalJSON() ([]byte, error) { +// MarshalJSON implements the json.Marshaller interface for type InnerError. +func (a InnerError) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) populate(objectMap, "code", a.Code) - populate(objectMap, "innererror", a.Innererror) + populate(objectMap, "innererror", a.InnerError) return json.Marshal(objectMap) } -// UnmarshalJSON implements the json.Unmarshaller interface for type azureCoreFoundationsInnerError. -func (a *azureCoreFoundationsInnerError) UnmarshalJSON(data []byte) error { +// UnmarshalJSON implements the json.Unmarshaller interface for type InnerError. +func (a *InnerError) UnmarshalJSON(data []byte) error { var rawMsg map[string]json.RawMessage if err := json.Unmarshal(data, &rawMsg); err != nil { return fmt.Errorf("unmarshalling type %T: %v", a, err) @@ -400,38 +703,7 @@ func (a *azureCoreFoundationsInnerError) UnmarshalJSON(data []byte) error { err = unpopulate(val, "Code", &a.Code) delete(rawMsg, key) case "innererror": - err = unpopulate(val, "Innererror", &a.Innererror) - delete(rawMsg, key) - } - if err != nil { - return fmt.Errorf("unmarshalling type %T: %v", a, err) - } - } - return nil -} - -// MarshalJSON implements the json.Marshaller interface for type azureCoreFoundationsInnerErrorInnererror. -func (a azureCoreFoundationsInnerErrorInnererror) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]any) - populate(objectMap, "code", a.Code) - populate(objectMap, "innererror", a.Innererror) - return json.Marshal(objectMap) -} - -// UnmarshalJSON implements the json.Unmarshaller interface for type azureCoreFoundationsInnerErrorInnererror. -func (a *azureCoreFoundationsInnerErrorInnererror) UnmarshalJSON(data []byte) error { - var rawMsg map[string]json.RawMessage - if err := json.Unmarshal(data, &rawMsg); err != nil { - return fmt.Errorf("unmarshalling type %T: %v", a, err) - } - for key, val := range rawMsg { - var err error - switch key { - case "code": - err = unpopulate(val, "Code", &a.Code) - delete(rawMsg, key) - case "innererror": - err = unpopulate(val, "Innererror", &a.Innererror) + err = unpopulate(val, "Innererror", &a.InnerError) delete(rawMsg, key) } if err != nil { @@ -534,6 +806,7 @@ func (c *ChatChoice) UnmarshalJSON(data []byte) error { // MarshalJSON implements the json.Marshaller interface for type ChatChoiceContentFilterResults. func (c ChatChoiceContentFilterResults) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) + populate(objectMap, "error", c.Error) populate(objectMap, "hate", c.Hate) populate(objectMap, "self_harm", c.SelfHarm) populate(objectMap, "sexual", c.Sexual) @@ -550,6 +823,9 @@ func (c *ChatChoiceContentFilterResults) UnmarshalJSON(data []byte) error { for key, val := range rawMsg { var err error switch key { + case "error": + err = unpopulate(val, "Error", &c.Error) + delete(rawMsg, key) case "hate": err = unpopulate(val, "Hate", &c.Hate) delete(rawMsg, key) @@ -662,7 +938,7 @@ func (c ChatCompletions) MarshalJSON() ([]byte, error) { populate(objectMap, "choices", c.Choices) populateTimeUnix(objectMap, "created", c.Created) populate(objectMap, "id", c.ID) - populate(objectMap, "prompt_annotations", c.PromptAnnotations) + populate(objectMap, "prompt_filter_results", c.PromptFilterResults) populate(objectMap, "usage", c.Usage) return json.Marshal(objectMap) } @@ -686,7 +962,9 @@ func (c *ChatCompletions) UnmarshalJSON(data []byte) error { err = unpopulate(val, "ID", &c.ID) delete(rawMsg, key) case "prompt_annotations": - err = unpopulate(val, "PromptAnnotations", &c.PromptAnnotations) + fallthrough + case "prompt_filter_results": + err = unpopulate(val, "PromptFilterResults", &c.PromptFilterResults) delete(rawMsg, key) case "usage": err = unpopulate(val, "Usage", &c.Usage) @@ -928,6 +1206,7 @@ func (c *Choice) UnmarshalJSON(data []byte) error { // MarshalJSON implements the json.Marshaller interface for type ChoiceContentFilterResults. func (c ChoiceContentFilterResults) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) + populate(objectMap, "error", c.Error) populate(objectMap, "hate", c.Hate) populate(objectMap, "self_harm", c.SelfHarm) populate(objectMap, "sexual", c.Sexual) @@ -944,6 +1223,9 @@ func (c *ChoiceContentFilterResults) UnmarshalJSON(data []byte) error { for key, val := range rawMsg { var err error switch key { + case "error": + err = unpopulate(val, "Error", &c.Error) + delete(rawMsg, key) case "hate": err = unpopulate(val, "Hate", &c.Hate) delete(rawMsg, key) @@ -1009,7 +1291,7 @@ func (c Completions) MarshalJSON() ([]byte, error) { populate(objectMap, "choices", c.Choices) populateTimeUnix(objectMap, "created", c.Created) populate(objectMap, "id", c.ID) - populate(objectMap, "prompt_annotations", c.PromptAnnotations) + populate(objectMap, "prompt_filter_results", c.PromptFilterResults) populate(objectMap, "usage", c.Usage) return json.Marshal(objectMap) } @@ -1033,7 +1315,9 @@ func (c *Completions) UnmarshalJSON(data []byte) error { err = unpopulate(val, "ID", &c.ID) delete(rawMsg, key) case "prompt_annotations": - err = unpopulate(val, "PromptAnnotations", &c.PromptAnnotations) + fallthrough + case "prompt_filter_results": + err = unpopulate(val, "PromptFilterResults", &c.PromptFilterResults) delete(rawMsg, key) case "usage": err = unpopulate(val, "Usage", &c.Usage) @@ -1199,55 +1483,16 @@ func (c *CompletionsUsage) UnmarshalJSON(data []byte) error { return nil } -// MarshalJSON implements the json.Marshaller interface for type ContentFilterResults. -func (c ContentFilterResults) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]any) - populate(objectMap, "hate", c.Hate) - populate(objectMap, "self_harm", c.SelfHarm) - populate(objectMap, "sexual", c.Sexual) - populate(objectMap, "violence", c.Violence) - return json.Marshal(objectMap) -} - -// UnmarshalJSON implements the json.Unmarshaller interface for type ContentFilterResults. -func (c *ContentFilterResults) UnmarshalJSON(data []byte) error { - var rawMsg map[string]json.RawMessage - if err := json.Unmarshal(data, &rawMsg); err != nil { - return fmt.Errorf("unmarshalling type %T: %v", c, err) - } - for key, val := range rawMsg { - var err error - switch key { - case "hate": - err = unpopulate(val, "Hate", &c.Hate) - delete(rawMsg, key) - case "self_harm": - err = unpopulate(val, "SelfHarm", &c.SelfHarm) - delete(rawMsg, key) - case "sexual": - err = unpopulate(val, "Sexual", &c.Sexual) - delete(rawMsg, key) - case "violence": - err = unpopulate(val, "Violence", &c.Violence) - delete(rawMsg, key) - } - if err != nil { - return fmt.Errorf("unmarshalling type %T: %v", c, err) - } - } - return nil -} - -// MarshalJSON implements the json.Marshaller interface for type ContentFilterResultsHate. -func (c ContentFilterResultsHate) MarshalJSON() ([]byte, error) { +// MarshalJSON implements the json.Marshaller interface for type ContentFilterResult. +func (c ContentFilterResult) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) populate(objectMap, "filtered", c.Filtered) populate(objectMap, "severity", c.Severity) return json.Marshal(objectMap) } -// UnmarshalJSON implements the json.Unmarshaller interface for type ContentFilterResultsHate. -func (c *ContentFilterResultsHate) UnmarshalJSON(data []byte) error { +// UnmarshalJSON implements the json.Unmarshaller interface for type ContentFilterResult. +func (c *ContentFilterResult) UnmarshalJSON(data []byte) error { var rawMsg map[string]json.RawMessage if err := json.Unmarshal(data, &rawMsg); err != nil { return fmt.Errorf("unmarshalling type %T: %v", c, err) @@ -1269,16 +1514,19 @@ func (c *ContentFilterResultsHate) UnmarshalJSON(data []byte) error { return nil } -// MarshalJSON implements the json.Marshaller interface for type ContentFilterResultsSelfHarm. -func (c ContentFilterResultsSelfHarm) MarshalJSON() ([]byte, error) { +// MarshalJSON implements the json.Marshaller interface for type ContentFilterResults. +func (c ContentFilterResults) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) - populate(objectMap, "filtered", c.Filtered) - populate(objectMap, "severity", c.Severity) + populate(objectMap, "error", c.Error) + populate(objectMap, "hate", c.Hate) + populate(objectMap, "self_harm", c.SelfHarm) + populate(objectMap, "sexual", c.Sexual) + populate(objectMap, "violence", c.Violence) return json.Marshal(objectMap) } -// UnmarshalJSON implements the json.Unmarshaller interface for type ContentFilterResultsSelfHarm. -func (c *ContentFilterResultsSelfHarm) UnmarshalJSON(data []byte) error { +// UnmarshalJSON implements the json.Unmarshaller interface for type ContentFilterResults. +func (c *ContentFilterResults) UnmarshalJSON(data []byte) error { var rawMsg map[string]json.RawMessage if err := json.Unmarshal(data, &rawMsg); err != nil { return fmt.Errorf("unmarshalling type %T: %v", c, err) @@ -1286,42 +1534,20 @@ func (c *ContentFilterResultsSelfHarm) UnmarshalJSON(data []byte) error { for key, val := range rawMsg { var err error switch key { - case "filtered": - err = unpopulate(val, "Filtered", &c.Filtered) + case "error": + err = unpopulate(val, "Error", &c.Error) delete(rawMsg, key) - case "severity": - err = unpopulate(val, "Severity", &c.Severity) + case "hate": + err = unpopulate(val, "Hate", &c.Hate) delete(rawMsg, key) - } - if err != nil { - return fmt.Errorf("unmarshalling type %T: %v", c, err) - } - } - return nil -} - -// MarshalJSON implements the json.Marshaller interface for type ContentFilterResultsSexual. -func (c ContentFilterResultsSexual) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]any) - populate(objectMap, "filtered", c.Filtered) - populate(objectMap, "severity", c.Severity) - return json.Marshal(objectMap) -} - -// UnmarshalJSON implements the json.Unmarshaller interface for type ContentFilterResultsSexual. -func (c *ContentFilterResultsSexual) UnmarshalJSON(data []byte) error { - var rawMsg map[string]json.RawMessage - if err := json.Unmarshal(data, &rawMsg); err != nil { - return fmt.Errorf("unmarshalling type %T: %v", c, err) - } - for key, val := range rawMsg { - var err error - switch key { - case "filtered": - err = unpopulate(val, "Filtered", &c.Filtered) + case "self_harm": + err = unpopulate(val, "SelfHarm", &c.SelfHarm) delete(rawMsg, key) - case "severity": - err = unpopulate(val, "Severity", &c.Severity) + case "sexual": + err = unpopulate(val, "Sexual", &c.Sexual) + delete(rawMsg, key) + case "violence": + err = unpopulate(val, "Violence", &c.Violence) delete(rawMsg, key) } if err != nil { @@ -1331,16 +1557,19 @@ func (c *ContentFilterResultsSexual) UnmarshalJSON(data []byte) error { return nil } -// MarshalJSON implements the json.Marshaller interface for type ContentFilterResultsViolence. -func (c ContentFilterResultsViolence) MarshalJSON() ([]byte, error) { +// MarshalJSON implements the json.Marshaller interface for type ContentFilterResultsError. +func (c ContentFilterResultsError) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) - populate(objectMap, "filtered", c.Filtered) - populate(objectMap, "severity", c.Severity) + populate(objectMap, "code", c.Code) + populate(objectMap, "details", c.Details) + populate(objectMap, "innererror", c.InnerError) + populate(objectMap, "message", c.Message) + populate(objectMap, "target", c.Target) return json.Marshal(objectMap) } -// UnmarshalJSON implements the json.Unmarshaller interface for type ContentFilterResultsViolence. -func (c *ContentFilterResultsViolence) UnmarshalJSON(data []byte) error { +// UnmarshalJSON implements the json.Unmarshaller interface for type ContentFilterResultsError. +func (c *ContentFilterResultsError) UnmarshalJSON(data []byte) error { var rawMsg map[string]json.RawMessage if err := json.Unmarshal(data, &rawMsg); err != nil { return fmt.Errorf("unmarshalling type %T: %v", c, err) @@ -1348,11 +1577,20 @@ func (c *ContentFilterResultsViolence) UnmarshalJSON(data []byte) error { for key, val := range rawMsg { var err error switch key { - case "filtered": - err = unpopulate(val, "Filtered", &c.Filtered) + case "code": + err = unpopulate(val, "Code", &c.Code) delete(rawMsg, key) - case "severity": - err = unpopulate(val, "Severity", &c.Severity) + case "details": + err = unpopulate(val, "Details", &c.Details) + delete(rawMsg, key) + case "innererror": + err = unpopulate(val, "Innererror", &c.InnerError) + delete(rawMsg, key) + case "message": + err = unpopulate(val, "Message", &c.Message) + delete(rawMsg, key) + case "target": + err = unpopulate(val, "Target", &c.Target) delete(rawMsg, key) } if err != nil { @@ -1749,6 +1987,7 @@ func (p *PromptFilterResult) UnmarshalJSON(data []byte) error { // MarshalJSON implements the json.Marshaller interface for type PromptFilterResultContentFilterResults. func (p PromptFilterResultContentFilterResults) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) + populate(objectMap, "error", p.Error) populate(objectMap, "hate", p.Hate) populate(objectMap, "self_harm", p.SelfHarm) populate(objectMap, "sexual", p.Sexual) @@ -1765,6 +2004,9 @@ func (p *PromptFilterResultContentFilterResults) UnmarshalJSON(data []byte) erro for key, val := range rawMsg { var err error switch key { + case "error": + err = unpopulate(val, "Error", &p.Error) + delete(rawMsg, key) case "hate": err = unpopulate(val, "Hate", &p.Hate) delete(rawMsg, key) @@ -1805,6 +2047,16 @@ func populateAny(m map[string]any, k string, v any) { } } +func populateByteArray(m map[string]any, k string, b []byte, f runtime.Base64Encoding) { + if azcore.IsNullValue(b) { + m[k] = nil + } else if len(b) == 0 { + return + } else { + m[k] = runtime.EncodeByteArray(b, f) + } +} + func unpopulate(data json.RawMessage, fn string, v any) error { if data == nil { return nil diff --git a/sdk/ai/azopenai/options.go b/sdk/ai/azopenai/options.go index 3dead1bb1924..10afca889f5e 100644 --- a/sdk/ai/azopenai/options.go +++ b/sdk/ai/azopenai/options.go @@ -15,6 +15,48 @@ type beginAzureBatchImageGenerationOptions struct { ResumeToken string } +// getAudioTranscriptionInternalOptions contains the optional parameters for the Client.getAudioTranscriptionInternal +// method. +type getAudioTranscriptionInternalOptions struct { + // The primary spoken language of the audio data to be transcribed, supplied as a two-letter ISO-639-1 language code such + // as 'en' or 'fr'. Providing this known input language is optional but may improve + // the accuracy and/or latency of transcription. + Language *string + + // The model to use for this transcription request. + Model *string + + // An optional hint to guide the model's style or continue from a prior audio segment. The written language of the prompt + // should match the primary spoken language of the audio data. + Prompt *string + + // The requested format of the transcription response data, which will influence the content and detail of the result. + ResponseFormat *AudioTranscriptionFormat + + // The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values + // like 0.2 will make it more focused and deterministic. If set to 0, the model will + // use log probability to automatically increase the temperature until certain thresholds are hit. + Temperature *float32 +} + +// getAudioTranslationInternalOptions contains the optional parameters for the Client.getAudioTranslationInternal method. +type getAudioTranslationInternalOptions struct { + // The model to use for this translation request. + Model *string + + // An optional hint to guide the model's style or continue from a prior audio segment. The written language of the prompt + // should match the primary spoken language of the audio data. + Prompt *string + + // The requested format of the translation response data, which will influence the content and detail of the result. + ResponseFormat *AudioTranslationFormat + + // The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values + // like 0.2 will make it more focused and deterministic. If set to 0, the model will + // use log probability to automatically increase the temperature until certain thresholds are hit. + Temperature *float32 +} + // GetChatCompletionsOptions contains the optional parameters for the Client.GetChatCompletions method. type GetChatCompletionsOptions struct { // placeholder for future optional parameters diff --git a/sdk/ai/azopenai/response_types.go b/sdk/ai/azopenai/response_types.go index b69aac060109..6c504a261576 100644 --- a/sdk/ai/azopenai/response_types.go +++ b/sdk/ai/azopenai/response_types.go @@ -14,6 +14,18 @@ type azureBatchImageGenerationInternalResponse struct { batchImageGenerationOperationResponse } +// getAudioTranscriptionInternalResponse contains the response from method Client.getAudioTranscriptionInternal. +type getAudioTranscriptionInternalResponse struct { + // Result information for an operation that transcribed spoken audio into written text. + AudioTranscription +} + +// getAudioTranslationInternalResponse contains the response from method Client.getAudioTranslationInternal. +type getAudioTranslationInternalResponse struct { + // Result information for an operation that transcribed spoken audio into written text. + AudioTranscription +} + // GetChatCompletionsResponse contains the response from method Client.GetChatCompletions. type GetChatCompletionsResponse struct { // Representation of the response data from a chat completions request. diff --git a/sdk/ai/azopenai/sample.env b/sdk/ai/azopenai/sample.env index 0a12cbb0ac5a..d05e39677b33 100644 --- a/sdk/ai/azopenai/sample.env +++ b/sdk/ai/azopenai/sample.env @@ -3,17 +3,26 @@ AOAI_ENDPOINT=https://.openai.azure.com/ AOAI_API_KEY= # These names will come from your model deployments in your Azure OpenAI resource -# ex: text-davinci-003 -AOAI_COMPLETIONS_MODEL_DEPLOYMENT= -# ex: gpt-4 -AOAI_CHAT_COMPLETIONS_MODEL_DEPLOYMENT= -AOAI_EMBEDDINGS_MODEL_DEPLOYMENT= +AOAI_COMPLETIONS_MODEL= +AOAI_CHAT_COMPLETIONS_MODEL= +AOAI_EMBEDDINGS_MODEL= + +# Azure OpenAI endpoints with a Whisper model deployed. +AOAI_ENDPOINT_WHISPER=https://.openai.azure.com/ +AOAI_API_KEY_WHISPER= +AOAI_MODEL_WHISPER= # public OpenAI OPENAI_ENDPOINT=https://api.openai.com/v1 OPENAI_API_KEY= -# ex: text-davinci-003 -OPENAI_COMPLETIONS_MODEL= -# ex: gpt-4 -OPENAI_CHAT_COMPLETIONS_MODEL= -OPENAI_EMBEDDINGS_MODEL= \ No newline at end of file +OPENAI_COMPLETIONS_MODEL= +OPENAI_CHAT_COMPLETIONS_MODEL= +OPENAI_EMBEDDINGS_MODEL= + +# These are used in the BYOD scenario. +COGNITIVE_SEARCH_API_KEY= +COGNITIVE_SEARCH_API_ENDPOINT=https://.search.windows.net +COGNITIVE_SEARCH_API_INDEX= + +# enable testing with TokenCredentials +USE_TOKEN_CREDS=true diff --git a/sdk/ai/azopenai/testdata/package-lock.json b/sdk/ai/azopenai/testdata/package-lock.json index 6cdfadef100e..6e2d706005fe 100644 --- a/sdk/ai/azopenai/testdata/package-lock.json +++ b/sdk/ai/azopenai/testdata/package-lock.json @@ -8,70 +8,68 @@ "name": "testdata", "version": "0.1.0", "dependencies": { - "@azure-tools/typespec-autorest": "^0.31.0", - "@azure-tools/typespec-azure-core": "^0.31.0", - "@typespec/compiler": "latest", - "@typespec/openapi3": "^0.45.0" + "@azure-tools/typespec-autorest": "0.34.0", + "@azure-tools/typespec-azure-core": "0.34.0", + "@typespec/compiler": "0.48.0", + "@typespec/openapi3": "0.48.0" } }, "node_modules/@azure-tools/typespec-autorest": { - "version": "0.31.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-autorest/-/typespec-autorest-0.31.0.tgz", - "integrity": "sha512-l/C4HyGr0ByC7FnlsoorXDIp46pbDxVPbq59XNX9sKJJ8p2297BJv7FdPlLi0BXGjEmzy93Ag4hoH9H/u54AhQ==", + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-autorest/-/typespec-autorest-0.34.0.tgz", + "integrity": "sha512-Fr5obMJzBgVzeK7pKblUKx1o7+p+KT84C1n+yRqqMP1Rqkq7y09iW3Mj3GO0xgs9DR8yMalBgHhvWWvB9l4yDA==", "engines": { "node": ">=16.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "~0.31.0", - "@typespec/compiler": "~0.45.0", - "@typespec/http": "~0.45.0", - "@typespec/openapi": "~0.45.0", - "@typespec/rest": "~0.45.0", - "@typespec/versioning": "~0.45.0" + "@azure-tools/typespec-azure-core": "~0.34.0", + "@typespec/compiler": "~0.48.0", + "@typespec/http": "~0.48.0", + "@typespec/openapi": "~0.48.0", + "@typespec/rest": "~0.48.0", + "@typespec/versioning": "~0.48.0" } }, "node_modules/@azure-tools/typespec-azure-core": { - "version": "0.31.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-core/-/typespec-azure-core-0.31.0.tgz", - "integrity": "sha512-sfJyRKGzQeBAm0Tw/CWFnWnHnxZDVbkXXLHeLb76VbRwkAu1P65eENRXXQTkUX5+PxnQH7qU/3MD5WT42AFsyA==", - "dependencies": { - "@typespec/lint": "~0.45.0" - }, + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-core/-/typespec-azure-core-0.34.0.tgz", + "integrity": "sha512-n3WrIx8bAHsknYXivbhl8WO+uzdB6RZMtx27/vnD+Jpo2krxLm0mMJK6pz2m/npTV4qlbY05OIeokhWQrneypw==", "engines": { "node": ">=16.0.0" }, "peerDependencies": { - "@typespec/compiler": "~0.45.0", - "@typespec/http": "~0.45.0", - "@typespec/rest": "~0.45.0" + "@typespec/compiler": "~0.48.0", + "@typespec/http": "~0.48.0", + "@typespec/rest": "~0.48.0" } }, "node_modules/@babel/code-frame": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", - "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz", + "integrity": "sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", - "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", + "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", "dependencies": { "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -111,23 +109,22 @@ } }, "node_modules/@typespec/compiler": { - "version": "0.45.2", - "resolved": "https://registry.npmjs.org/@typespec/compiler/-/compiler-0.45.2.tgz", - "integrity": "sha512-Te2mj24Sh0MinXPzPLINXRrjMuvuu2AsrBWrDWYjTgodt6MMRj5HiovxByFcYIjWUL3U4sHdKPXcOnAunDsd+Q==", + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@typespec/compiler/-/compiler-0.48.0.tgz", + "integrity": "sha512-+BEeSLl7unxtRpC1L8sbTu5A94WIVQaYSFf0egkJ0panN0wWzcFbk4SJiSa9wxjDTr9fh2elSrRVk2t1XTk2nQ==", "dependencies": { - "@babel/code-frame": "~7.21.4", + "@babel/code-frame": "~7.22.5", "ajv": "~8.12.0", "change-case": "~4.1.2", "globby": "~13.1.1", - "js-yaml": "~4.1.0", - "mkdirp": "~2.1.6", "mustache": "~4.2.0", - "node-watch": "~0.7.1", "picocolors": "~1.0.0", - "prettier": "~2.8.7", + "prettier": "~3.0.1", "prompts": "~2.4.1", + "semver": "^7.3.8", "vscode-languageserver": "~8.1.0", "vscode-languageserver-textdocument": "~1.0.1", + "yaml": "~2.3.1", "yargs": "~17.7.1" }, "bin": { @@ -139,82 +136,71 @@ } }, "node_modules/@typespec/http": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@typespec/http/-/http-0.45.0.tgz", - "integrity": "sha512-D9B+CzDqoIvlerQL5R7k367R5pwvX5Ic/6YE3bkMzfq9G40TRz5ExpOf+ASmgRbKrWjm/0ppdE4IlRMCI6qFmA==", + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@typespec/http/-/http-0.48.0.tgz", + "integrity": "sha512-e+0Y0Ky71flUNZSRzCfoOm8XvXsSYGmQgB9VZFDbLl8mQlXwuTfib4tWrU531TCtZHMnylbXx2wAk5+3uC6b9g==", "peer": true, "engines": { "node": ">=16.0.0" }, "peerDependencies": { - "@typespec/compiler": "~0.45.0" - } - }, - "node_modules/@typespec/lint": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@typespec/lint/-/lint-0.45.0.tgz", - "integrity": "sha512-dACuEDQD1CFLftiKIcbWrARMb7lKEXMKE+GzsSa39Xogxv4FH6wc932tPwcMXXTdpfDO2dWUoquEcvXHxUAYKQ==", - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@typespec/compiler": "~0.45.0" + "@typespec/compiler": "~0.48.0" } }, "node_modules/@typespec/openapi": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@typespec/openapi/-/openapi-0.45.0.tgz", - "integrity": "sha512-6W6DMCiXb2iLH4TLgI/u8FFS5v/oBu0DZF2BER6Pzx6v5mURmYGjXiwrQ+DrkOXtqb0YLZMuDU1s9CXQe6P87Q==", + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@typespec/openapi/-/openapi-0.48.0.tgz", + "integrity": "sha512-KptMNQd/+olEetmNGend6jhMjnFa+Lrhw/M+HCP46HcKH/NDVA/RWtX/KcT4KjxJYrmTlRF9sz19/Efg7u02CA==", "peer": true, "engines": { "node": ">=16.0.0" }, "peerDependencies": { - "@typespec/compiler": "~0.45.0", - "@typespec/http": "~0.45.0", - "@typespec/rest": "~0.45.0" + "@typespec/compiler": "~0.48.0", + "@typespec/http": "~0.48.0", + "@typespec/rest": "~0.48.0" } }, "node_modules/@typespec/openapi3": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@typespec/openapi3/-/openapi3-0.45.0.tgz", - "integrity": "sha512-D5zBr88a+jpYFRGt01tGR0PiiLE6tazyAvayQ3+MCByZbHeQ+YDCmKgf6lbGtHbN4p1FqBhK+JrzH/42cXHYEw==", + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@typespec/openapi3/-/openapi3-0.48.0.tgz", + "integrity": "sha512-2ZiAvN4/LLS8Lt+tju3wKSNeDD8eTXNCaTxHw61jGVvPiCtU7E/HyF+eA2pMlfXAtjlEzpbuQb+rF3eaex1qUA==", "dependencies": { - "js-yaml": "~4.1.0" + "yaml": "~2.3.1" }, "engines": { "node": ">=16.0.0" }, "peerDependencies": { - "@typespec/compiler": "~0.45.0", - "@typespec/http": "~0.45.0", - "@typespec/openapi": "~0.45.0", - "@typespec/rest": "~0.45.0", - "@typespec/versioning": "~0.45.0" + "@typespec/compiler": "~0.48.0", + "@typespec/http": "~0.48.0", + "@typespec/openapi": "~0.48.0", + "@typespec/rest": "~0.48.0", + "@typespec/versioning": "~0.48.0" } }, "node_modules/@typespec/rest": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@typespec/rest/-/rest-0.45.0.tgz", - "integrity": "sha512-u9vFmXvoKdkffh0I2LDDPAKNpILuaxu/aaMRdLEw1Zfmes2mWDruReMjPU8piRB+hE5eDDxU4INPtudy2D61tA==", + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@typespec/rest/-/rest-0.48.0.tgz", + "integrity": "sha512-PM41o2a7qsTi6OIiCE53OB5uh+GTas8YObJjV5Z9JHYtHhQKVQaRHE72qoZQp3919vJNStXTdDEbIjzMIVt3Ow==", "peer": true, "engines": { "node": ">=16.0.0" }, "peerDependencies": { - "@typespec/compiler": "~0.45.0" + "@typespec/compiler": "~0.48.0" } }, "node_modules/@typespec/versioning": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@typespec/versioning/-/versioning-0.45.0.tgz", - "integrity": "sha512-Hocyi9AAuPu9az7Aw4GiWk5CUhq9CQBx8KEDVqIEBI1AvZACPNqJU02TClSyMSKWZY6V/2Gb8lxvPyNvyiF8Hw==", + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@typespec/versioning/-/versioning-0.48.0.tgz", + "integrity": "sha512-WF26vmMPwizhSnjX0ox23nbp7hthtB4cN/J5w1tlryXyp/BXySHoYsJEMK7fviSpj4WdreVXdM6wmRIG33zqig==", "peer": true, "engines": { "node": ">=16.0.0" }, "peerDependencies": { - "@typespec/compiler": "~0.45.0" + "@typespec/compiler": "~0.48.0" } }, "node_modules/ajv": { @@ -251,11 +237,6 @@ "node": ">=4" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, "node_modules/braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", @@ -401,9 +382,9 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -536,17 +517,6 @@ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -568,6 +538,17 @@ "tslib": "^2.0.3" } }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -588,20 +569,6 @@ "node": ">=8.6" } }, - "node_modules/mkdirp": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", - "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/mustache": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", @@ -619,14 +586,6 @@ "tslib": "^2.0.3" } }, - "node_modules/node-watch": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/node-watch/-/node-watch-0.7.3.tgz", - "integrity": "sha512-3l4E8uMPY1HdMMryPRUAl+oIHtXtyiTlIiESNSVSNxcPfzAFzeTbXFQkZfAwBbo0B1qMSG8nUABx+Gd+YrbKrQ==", - "engines": { - "node": ">=6" - } - }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", @@ -679,14 +638,14 @@ } }, "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" @@ -778,6 +737,20 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/sentence-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", @@ -860,9 +833,9 @@ } }, "node_modules/tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/upper-case": { "version": "2.0.2", @@ -980,6 +953,19 @@ "node": ">=10" } }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yaml": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz", + "integrity": "sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==", + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/sdk/ai/azopenai/testdata/package.json b/sdk/ai/azopenai/testdata/package.json index b11bb3836a51..d40aa8a7d787 100644 --- a/sdk/ai/azopenai/testdata/package.json +++ b/sdk/ai/azopenai/testdata/package.json @@ -7,10 +7,10 @@ "build": "tsp compile ./TempTypeSpecFiles/OpenAI.Inference" }, "dependencies": { - "@azure-tools/typespec-autorest": "^0.31.0", - "@azure-tools/typespec-azure-core": "^0.31.0", - "@typespec/compiler": "latest", - "@typespec/openapi3": "^0.45.0" + "@azure-tools/typespec-autorest": "0.34.0", + "@azure-tools/typespec-azure-core": "0.34.0", + "@typespec/compiler": "0.48.0", + "@typespec/openapi3": "0.48.0" }, "private": true -} \ No newline at end of file +} diff --git a/sdk/ai/azopenai/testdata/sampledata_audiofiles_myVoiceIsMyPassportVerifyMe01.mp3 b/sdk/ai/azopenai/testdata/sampledata_audiofiles_myVoiceIsMyPassportVerifyMe01.mp3 new file mode 100644 index 000000000000..6bb67a4fea37 Binary files /dev/null and b/sdk/ai/azopenai/testdata/sampledata_audiofiles_myVoiceIsMyPassportVerifyMe01.mp3 differ diff --git a/sdk/ai/azopenai/testdata/tsp-location.yaml b/sdk/ai/azopenai/testdata/tsp-location.yaml index b8435e327e32..8720b2b03468 100644 --- a/sdk/ai/azopenai/testdata/tsp-location.yaml +++ b/sdk/ai/azopenai/testdata/tsp-location.yaml @@ -1,4 +1,5 @@ #location: https://github.com/Azure/azure-rest-api-specs/tree/1393b6e34d7370733e3e2236c4df686280a96f36/specification/cognitiveservices/OpenAI.Inference directory: specification/cognitiveservices/OpenAI.Inference -commit: b646a42aa3b7a0ce488d05f1724827ea41d12cf1 +#commit: 90600bad43a463e874ab3bee0064302373933d4a +commit: 1e243e2b0d0d006599dcb64f82fd92aecc1247be repo: Azure/azure-rest-api-specs diff --git a/sdk/ai/azopenai/version.go b/sdk/ai/azopenai/version.go index fb0ff5cf9426..425e296b22b7 100644 --- a/sdk/ai/azopenai/version.go +++ b/sdk/ai/azopenai/version.go @@ -7,5 +7,5 @@ package azopenai const ( - version = "v0.2.1" + version = "v0.3.0" )