From cdf0177fb90cb938ea340a5f4daba4d7882f88db Mon Sep 17 00:00:00 2001 From: Gerardo Lecaros <10088504+glecaros@users.noreply.github.com> Date: Tue, 10 Sep 2024 11:47:12 -0700 Subject: [PATCH] [azopenai] Updates for 2024-07-01 (#23362) * Regenerated to accomodate the latest API version (2024-07-01-preview), which adds in new batching and file APIs. * Updated help text for the function parameters. * Tests were literally doing nothing and weren't needed - we create clients all over the place using all the constructors. * Remove empty section. * Upload files works, and the other routes related to it _should_ work. Still need to test some more. * Fixing a bug where I didn't need to pass in a pointer anymore. * Very basic test for batches. The timing of the API makes it hard for us to test normally. * Updated to handle an anonymous type * Using latest commit from PR. * updates for merged spec. * fixed generation * removing orphaned model * updated recordings * skipping test * reducing coverage until we can reenable file tests. * added comment about workaround * removing orphaned type * updated changelog --------- Co-authored-by: Richard Park --- eng/config.json | 2 +- sdk/ai/azopenai/CHANGELOG.md | 37 +- sdk/ai/azopenai/assets.json | 2 +- sdk/ai/azopenai/autorest.md | 294 ++++++-- sdk/ai/azopenai/client.go | 461 +++++++++++- sdk/ai/azopenai/client_batch_files_test.go | 85 +++ sdk/ai/azopenai/client_custom_files.go | 121 ++++ sdk/ai/azopenai/client_functions_test.go | 106 ++- sdk/ai/azopenai/client_shared_test.go | 10 + sdk/ai/azopenai/constants.go | 114 +++ sdk/ai/azopenai/custom_client.go | 62 +- sdk/ai/azopenai/custom_client_audio.go | 2 +- sdk/ai/azopenai/custom_client_pagers.go | 23 + sdk/ai/azopenai/custom_client_test.go | 58 -- .../example_client_getchatcompletions_test.go | 80 ++- sdk/ai/azopenai/models.go | 291 +++++++- sdk/ai/azopenai/models_serde.go | 662 +++++++++++++++++- sdk/ai/azopenai/options.go | 51 ++ sdk/ai/azopenai/pager.go | 64 ++ sdk/ai/azopenai/polymorphic_helpers.go | 23 - sdk/ai/azopenai/responses.go | 53 ++ sdk/ai/azopenai/testdata/package-lock.json | 481 ++++++++++--- sdk/ai/azopenai/testdata/package.json | 8 +- sdk/ai/azopenai/testdata/tsp-location.yaml | 2 +- 24 files changed, 2673 insertions(+), 419 deletions(-) create mode 100644 sdk/ai/azopenai/client_batch_files_test.go create mode 100644 sdk/ai/azopenai/client_custom_files.go create mode 100644 sdk/ai/azopenai/custom_client_pagers.go create mode 100644 sdk/ai/azopenai/pager.go diff --git a/eng/config.json b/eng/config.json index 02dc38146b..59889b6ab0 100644 --- a/eng/config.json +++ b/eng/config.json @@ -46,7 +46,7 @@ }, { "Name": "azopenai", - "CoverageGoal": 0.22 + "CoverageGoal": 0.18 }, { "Name": "ai/azopenaiassistants", diff --git a/sdk/ai/azopenai/CHANGELOG.md b/sdk/ai/azopenai/CHANGELOG.md index 35f8c9c859..936c357acc 100644 --- a/sdk/ai/azopenai/CHANGELOG.md +++ b/sdk/ai/azopenai/CHANGELOG.md @@ -1,14 +1,45 @@ # Release History -## 0.6.2 (Unreleased) +## 0.6.2 (2024-09-10) ### Features Added +- Added Batch and File APIs. + ### Breaking Changes -### Bugs Fixed +- FunctionDefinition.Parameters has been changed to take JSON instead of an object/map. You can set it using code +similar to this: -### Other Changes + ```go + parametersJSON, err := json.Marshal(map[string]any{ + "required": []string{"location"}, + "type": "object", + "properties": map[string]any{ + "location": map[string]any{ + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + }, + }, + }) + + if err != nil { + // TODO: Update the following line with your application specific error handling logic + log.Printf("ERROR: %s", err) + return + } + + // and then, in ChatCompletionsOptions + opts := azopenai.ChatCompletionsOptions{ + Functions: []azopenai.FunctionDefinition{ + { + Name: to.Ptr("get_current_weather"), + Description: to.Ptr("Get the current weather in a given location"), + Parameters: parametersJSON, + }, + }, + } + ``` ## 0.6.1 (2024-08-14) diff --git a/sdk/ai/azopenai/assets.json b/sdk/ai/azopenai/assets.json index e2c9d08367..19920c5b53 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_1784620173" + "Tag": "go/ai/azopenai_920fad77c4" } diff --git a/sdk/ai/azopenai/autorest.md b/sdk/ai/azopenai/autorest.md index a780250a06..83c3c472e9 100644 --- a/sdk/ai/azopenai/autorest.md +++ b/sdk/ai/azopenai/autorest.md @@ -49,7 +49,7 @@ directive: "description": "The model to use for this request." } return $; - - from: + - from: - models.go - models_serde.go - options.go @@ -60,7 +60,7 @@ directive: .replace(/populate\(objectMap, "model", (.)\.Model\)/g, 'populate(objectMap, "model", $1.DeploymentName)') .replace(/err = unpopulate\(val, "Model", &(.)\.Model\)/g, 'err = unpopulate(val, "Model", &$1.DeploymentName)') .replace(/Model:/g, "DeploymentName: "); - # hack - we have _one_ spot where we want to keep it as Model (ChatCompletions.Model) since + # hack - we have _one_ spot where we want to keep it as Model (ChatCompletions.Model) since # it is the actual model name, not the deployment, in Azure OpenAI or OpenAI. - from: models.go where: $ @@ -75,7 +75,7 @@ directive: ## Polymorphic adjustments -The polymorphic _input_ models all expose the discriminator but it's ignored when serializing +The polymorphic _input_ models all expose the discriminator but it's ignored when serializing (ie, each type already knows the value and fills it in). So we'll just hide it. `ChatRequestMessageClassification.Role` @@ -145,7 +145,7 @@ directive: - from: swagger-document where: $.definitions.AzureChatExtensionDataSourceResponseCitation transform: $.properties.filepath["x-ms-client-name"] = "FilePath" - + # FilepathField -> FilePathField - from: swagger-document where: $.definitions.AzureCosmosDBFieldMappingOptions @@ -166,15 +166,15 @@ directive: transform: $.properties.custom_blocklists["x-ms-client-name"] = "CustomBlockLists" - from: swagger-document where: $.definitions.ContentFilterResultDetailsForPrompt - transform: $.properties.custom_blocklists["x-ms-client-name"] = "CustomBlockLists" + transform: $.properties.custom_blocklists["x-ms-client-name"] = "CustomBlockLists" - from: swagger-document where: $.definitions.ContentFilterResultsForChoice - transform: $.properties.custom_blocklists["x-ms-client-name"] = "CustomBlockLists" + transform: $.properties.custom_blocklists["x-ms-client-name"] = "CustomBlockLists" ``` ## Cleanup the audio transcription APIs -We're wrapping the audio translation and transcription APIs, so we can eliminate some of +We're wrapping the audio translation and transcription APIs, so we can eliminate some of these autogenerated models and functions. ```yaml @@ -189,11 +189,13 @@ directive: 'XMSPathsHksgfdDeploymentsDeploymentidAudioTranscriptionsOverloadGetaudiotranscriptionasresponseobjectPostRequestbodyContentMultipartFormDataSchema', 'XMSPaths1Ak7Ov3DeploymentsDeploymentidAudioTranslationsOverloadGetaudiotranslationasresponseobjectPostRequestbodyContentMultipartFormDataSchema', 'Paths1G1Yr9HDeploymentsDeploymentidAudioTranslationsPostRequestbodyContentMultipartFormDataSchema', - 'Paths1MlipaDeploymentsDeploymentidAudioTranscriptionsPostRequestbodyContentMultipartFormDataSchema' + 'Paths1MlipaDeploymentsDeploymentidAudioTranscriptionsPostRequestbodyContentMultipartFormDataSchema', + 'Paths1Filz8PFilesPostRequestbodyContentMultipartFormDataSchema', + 'BatchCreateResponse', ]; for (let name of typesToRemove) { - $ = $.replace(new RegExp(`type ${name} struct.+?\n}`, "s"), "") + $ = $.replace(new RegExp(`(// ${name} - [\\w\\s\\.,]+\n)?type ${name} struct.+?\s*\}`, "s"), "") .replace(new RegExp(`// MarshalJSON implements the json.Marshaller interface for type ${name}.+?\n}`, "s"), "") .replace(new RegExp(`// UnmarshalJSON implements the json.Unmarshaller interface for type ${name}.+?\n}`, "s"), ""); } @@ -227,7 +229,7 @@ directive: # fix any calls that don't use 'deploymentID' - from: client.go where: $ - transform: | + transform: | return $ .replace(/, deploymentID string,/g, ",") .replace(/ctx, deploymentID, /g, "ctx, ") @@ -235,12 +237,7 @@ directive: ## Move the Azure extensions into their own section of the options -```yaml -# TODO: rename and move. -``` - - -We've moved these 'extension' data types into their own field. +We've moved these 'extension' data types into their own field. ```yaml directive: @@ -250,13 +247,13 @@ directive: ``` ## Trim the Error object to match our Go conventions - + ```yaml directive: - from: swagger-document where: $.definitions["Azure.Core.Foundations.Error"] transform: | - $.properties = { + $.properties = { code: $.properties["code"], message: { ...$.properties["message"], @@ -264,12 +261,12 @@ directive: }, }; $["x-ms-client-name"] = "Error"; - + - from: swagger-document where: $.definitions transform: delete $["Azure.Core.Foundations.InnerError"]; - - from: + - from: - models.go - models_serde.go where: $ @@ -284,9 +281,25 @@ directive: - from: client.go where: $ transform: | - return $ - .replace(/runtime\.JoinPaths\(client.endpoint, urlPath\)/g, "client.formatURL(urlPath, getDeployment(body))"); + const urlPaths = [ + ".+?/audio/speech", + ".+?/audio/transcriptions", + ".+?/audio/translations", + ".+?/chat/completions", + ".+?/completions", + ".+?/embeddings", + ".+?/images/generations" + ].join("|"); + const re = new RegExp( + '(urlPath := "(?:' + urlPaths + ')"\\s+' + + '.+?)runtime\.JoinPaths\\(client\\.endpoint, urlPath\\)', + 'gs'); + + return $.replace(re, "$1client.formatURL(urlPath, getDeployment(body))"); + - from: client.go + where: $ + transform: return $.replace(/runtime\.JoinPaths\(client\.endpoint, urlPath\)/g, "client.formatURL(urlPath, nil)"); # Allow custom parsing of the returned error, mostly for handling the content filtering errors. - from: client.go where: $ @@ -315,7 +328,7 @@ directive: transform: delete $.properties.stream; - from: swagger-document where: $.definitions["ChatCompletionsOptions"] - transform: delete $.properties.stream; + transform: delete $.properties.stream; ``` Changes for audio/whisper APIs. @@ -339,7 +352,7 @@ directive: transform: $.operationId = "GetAudioTranscriptionInternal" # hide the generated functions, in favor of our public wrappers. - - from: + - from: - client.go - models.go - models_serde.go @@ -356,8 +369,9 @@ directive: where: $ transform: | return $ - .replace(/(func.*getAudio(?:Translation|Transcription)InternalCreateRequest\(.+?)options/g, "$1body") - .replace(/runtime\.SetMultipartFormData\(.+?\)/sg, "setMultipartFormData(req, file, *body)") + .replace(/(func.* getAudio(?:Translation|Transcription)InternalCreateRequest\(.+?)options/g, "$1body") + .replace(/(func.* uploadFileCreateRequest\(.+?)options/g, "$1body") + .replace(/runtime\.SetMultipartFormData\(.+?\)/sg, "setMultipartFormData(req, file, *body)"); # response type parsing (can be text/plain _or_ JSON) - from: client.go @@ -386,7 +400,7 @@ directive: return $ .replace(/AvgLogprob \*float32/g, "AvgLogProb *float32") .replace(/(a|c)\.AvgLogprob/g, "$1.AvgLogProb") - - from: + - from: - client.go - models.go - models_serde.go @@ -410,7 +424,7 @@ directive: where: $ transform: >- return $.replace( - /(\s+)urlPath\s*:=\s*"\/deployments\/\{deploymentId\}\/([^"]+)".+?url\.PathEscape.+?\n/gs, + /(\s+)urlPath\s*:=\s*"\/deployments\/\{deploymentId\}\/([^"]+)".+?url\.PathEscape.+?\n/gs, "$1urlPath := \"$2\"\n") # splice out the auto-generated `deploymentID` field from the client @@ -418,14 +432,14 @@ directive: where: $ transform: >- return $.replace( - /(type Client struct[^}]+})/s, + /(type Client struct[^}]+})/s, "type Client struct {\ninternal *azcore.Client; clientData;\n}") - - from: + - from: - models_serde.go - models.go where: $ - transform: | + transform: | return $ // remove some types that were generated to support the recursive error. .replace(/\/\/ AzureCoreFoundationsInnerErrorInnererror.+?\n}/s, "") @@ -446,7 +460,7 @@ directive: // 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. // @@ -455,7 +469,7 @@ directive: where: $ transform: >- return $.replace( - /type ServiceAPIVersions string.+PossibleServiceAPIVersionsValues.+?\n}/gs, + /type ServiceAPIVersions string.+PossibleServiceAPIVersionsValues.+?\n}/gs, "") # delete client name prefix from method options and response types @@ -489,16 +503,6 @@ directive: transform: return $.replace(/case "prompt_filter_results":/g, 'case "prompt_annotations":\nfallthrough\ncase "prompt_filter_results":') ``` -Update the ChatRequestUserMessage to allow for []ChatCompletionRequestMessageContentPartText _or_ -a string. - -```yaml -directive: - - from: models.go - where: $ - transform: return $.replace(/Content any/g, 'Content ChatRequestUserMessageContent') -``` - Add in some types that are incorrectly not being exported in the generation ```yaml @@ -625,43 +629,14 @@ directive: - from: polymorphic_helpers.go where: $ transform: | - return $.replace(/(func unmarshalChatCompletionsToolCallClassification.+?var b ChatCompletionsToolCallClassification\n)/s, - `$1\n` + + return $.replace(/(func unmarshalChatCompletionsToolCallClassification.+?var b ChatCompletionsToolCallClassification\n)/s, + `$1\n` + `if m["type"] == nil && m["function"] != nil {\n` + ` // WORKAROUND: the streaming results don't contain the proper role for functions, so we need to add these in.\n` + - ` m["type"] = string(ChatRoleFunction)\n` + + ` m["type"] = string(ChatRoleFunction)\n` + `}\n`); ``` -Fix ToolChoice discriminated union - -```yaml -directive: - - from: swagger-document - where: $.definitions.ChatCompletionsOptions.properties - transform: $["tool_choice"]["x-ms-client-name"] = "ToolChoiceRenameMe" - - from: - - models.go - - models_serde.go - where: $ - transform: | - return $ - .replace(/^\s+ToolChoiceRenameMe.+$/m, "ToolChoice *ChatCompletionsToolChoice") // update the name _and_ type for the field - .replace(/ToolChoiceRenameMe/g, "ToolChoice") // rename all other references - .replace(/populateAny\(objectMap, "tool_choice", c\.ToolChoice\)/, 'populate(objectMap, "tool_choice", c.ToolChoice)'); // treat field as typed so nil means omit. -``` - -```yaml -directive: - - from: models.go - where: $ - transform: return $.replace(/FunctionCall any/, "FunctionCall *ChatCompletionsOptionsFunctionCall"); - - from: models_serde.go - where: $ - transform: return $.replace(/populateAny\(objectMap, "function_call", c\.FunctionCall\)/, 'populate(objectMap, "function_call", c.FunctionCall)'); -``` - - ```yaml directive: - from: models_serde.go @@ -684,8 +659,173 @@ directive: - from: models.go where: $ transform: | - const text = "// - If using EmbeddingEncodingFormatFloat (the default), the value will be a []float32, in [EmbeddingItem.Embedding]\n" + + const text = "// - If using EmbeddingEncodingFormatFloat (the default), the value will be a []float32, in [EmbeddingItem.Embedding]\n" + "// - If using EmbeddingEncodingFormatBase64, the value will be a base-64 string in [EmbeddingItem.EmbeddingBase64]\n"; - + return $.replace(/(EncodingFormat \*EmbeddingEncodingFormat)/, `${text}$1`); ``` + +Update docs for FunctionDefinition.Parameters to indicate it's intended to be serialized JSON bytes, +not an object or map[string]any. + +```yaml +directive: + - from: models.go + where: $ + transform: | + const comment = ` // REQUIRED; The function definition details for the function tool. \n` + + ` // NOTE: this field is JSON text that describes a JSON schema. You can marshal a data\n` + + ` // structure using code similar to this:\n` + + ` //\n` + + ` // jsonBytes, err := json.Marshal(map[string]any{\n` + + ` // "required": []string{"location"},\n` + + ` // "type": "object",\n` + + ` // "properties": map[string]any{\n` + + ` // "location": map[string]any{\n` + + ` // "type": "string",\n` + + ` // "description": "The city and state, e.g. San Francisco, CA",\n` + + ` // },\n` + + ` // },\n` + + ` // })\n` + + ` //\n` + + ` // if err != nil {\n` + + ` // panic(err)\n` + + ` // }\n` + + ` // \n` + + ` // funcDef := &azopenai.FunctionDefinition{\n` + + ` // Name: to.Ptr("get_current_weather"),\n` + + ` // Description: to.Ptr("Get the current weather in a given location"),\n` + + ` // Parameters: jsonBytes,\n` + + ` // }`; + return $.replace(/Parameters \[\]byte/, comment + "\nParameters []byte"); +``` + +## Unions + +Update the ChatRequestUserMessage to allow for []ChatCompletionRequestMessageContentPartText _or_ +a string. + +```yaml +directive: + - from: swagger-document + where: $.definitions + transform: | + $["ChatRequestUserMessageContent"] = { + "x-ms-external": true, + "type": "object", "properties": { "stub": { "type": "string" }} + }; + return $; + - from: swagger-document + where: $.definitions.ChatRequestUserMessage.properties.content + transform: $["$ref"] = "#/definitions/ChatRequestUserMessageContent"; return $; +``` + +*ChatCompletionsToolChoice + +```yaml +directive: + - from: swagger-document + where: $.definitions + transform: | + $["ChatCompletionsToolChoice"] = { + "x-ms-external": true, + "type": "object", "properties": { "stub": { "type": "string" }} + }; + return $; + - from: swagger-document + where: $.definitions.ChatCompletionsOptions.properties.tool_choice + transform: $["$ref"] = "#/definitions/ChatCompletionsToolChoice"; return $; +``` + +*ChatCompletionsOptionsFunctionCall + +```yaml +directive: + - from: swagger-document + where: $.definitions + transform: | + $["ChatCompletionsOptionsFunctionCall"] = { + "x-ms-external": true, + "type": "object", "properties": { "stub": { "type": "string" }} + }; + return $; + - from: swagger-document + where: $.definitions.ChatCompletionsOptions.properties.function_call + transform: $["$ref"] = "#/definitions/ChatCompletionsOptionsFunctionCall"; return $; +``` + + + + + + +## Pagers + +ListBatches is a pageable API. + +```yaml +directive: + - from: client.go + where: $ + transform: return $ + .replace(/ListBatches([ (])/g, "listBatches$1") + .replace(/result\.ListBatchesResponse/g, "result.ListBatchesPage"); + - from: responses.go + where: $ + transform: return $.replace(/ListBatchesResponse\s+\}/g, "ListBatchesPage\n}"); + - from: + - models.go + - models_serde.go + where: $ + transform: return $ + .replace(/ ListBatchesResponse/g, " ListBatchesPage") + .replace(/ListBatchesResponse\)/g, " ListBatchesPage)"); +``` + +## Files + +```yaml +directive: + - from: client.go + where: $ + transform: return $.replace(/\/\/ uploadFileCreateRequest creates .+?return req, nil\s+}/s, ""); +``` + +## Anonymous types + +Give names to anonymous types + +```yaml +directive: + - from: swagger-document + where: $.paths['/batches'].get.responses['200'].schema + transform: $["x-ms-client-name"] = "ListBatchesPage"; return $; +``` diff --git a/sdk/ai/azopenai/client.go b/sdk/ai/azopenai/client.go index c93b17450b..61bbfbdf96 100644 --- a/sdk/ai/azopenai/client.go +++ b/sdk/ai/azopenai/client.go @@ -10,8 +10,12 @@ package azopenai import ( "context" + "errors" "io" "net/http" + "net/url" + "strconv" + "strings" "github.com/Azure/azure-sdk-for-go/sdk/azcore" "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" @@ -25,10 +29,155 @@ type Client struct { clientData } +// CancelBatch - Gets details for a single batch specified by the given batchID. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-07-01-preview +// - batchID - The identifier of the batch. +// - options - CancelBatchOptions contains the optional parameters for the Client.CancelBatch method. +func (client *Client) CancelBatch(ctx context.Context, batchID string, options *CancelBatchOptions) (CancelBatchResponse, error) { + var err error + req, err := client.cancelBatchCreateRequest(ctx, batchID, options) + if err != nil { + return CancelBatchResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return CancelBatchResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK) { + err = client.newError(httpResp) + return CancelBatchResponse{}, err + } + resp, err := client.cancelBatchHandleResponse(httpResp) + return resp, err +} + +// cancelBatchCreateRequest creates the CancelBatch request. +func (client *Client) cancelBatchCreateRequest(ctx context.Context, batchID string, options *CancelBatchOptions) (*policy.Request, error) { + urlPath := "/batches/{batchId}/cancel" + if batchID == "" { + return nil, errors.New("parameter batchID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{batchId}", url.PathEscape(batchID)) + req, err := runtime.NewRequest(ctx, http.MethodPost, client.formatURL(urlPath, nil)) + if err != nil { + return nil, err + } + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// cancelBatchHandleResponse handles the CancelBatch response. +func (client *Client) cancelBatchHandleResponse(resp *http.Response) (CancelBatchResponse, error) { + result := CancelBatchResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.Batch); err != nil { + return CancelBatchResponse{}, err + } + return result, nil +} + +// CreateBatch - Creates and executes a batch from an uploaded file of requests. Response includes details of the enqueued +// job including job status. The ID of the result file is added to the response once complete. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-07-01-preview +// - createBatchRequest - The specification of the batch to create and execute. +// - options - CreateBatchOptions contains the optional parameters for the Client.CreateBatch method. +func (client *Client) CreateBatch(ctx context.Context, createBatchRequest BatchCreateRequest, options *CreateBatchOptions) (CreateBatchResponse, error) { + var err error + req, err := client.createBatchCreateRequest(ctx, createBatchRequest, options) + if err != nil { + return CreateBatchResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return CreateBatchResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusCreated) { + err = client.newError(httpResp) + return CreateBatchResponse{}, err + } + resp, err := client.createBatchHandleResponse(httpResp) + return resp, err +} + +// createBatchCreateRequest creates the CreateBatch request. +func (client *Client) createBatchCreateRequest(ctx context.Context, createBatchRequest BatchCreateRequest, options *CreateBatchOptions) (*policy.Request, error) { + urlPath := "/batches" + req, err := runtime.NewRequest(ctx, http.MethodPost, client.formatURL(urlPath, nil)) + if err != nil { + return nil, err + } + req.Raw().Header["Accept"] = []string{"application/json"} + if err := runtime.MarshalAsJSON(req, createBatchRequest); err != nil { + return nil, err + } + return req, nil +} + +// createBatchHandleResponse handles the CreateBatch response. +func (client *Client) createBatchHandleResponse(resp *http.Response) (CreateBatchResponse, error) { + result := CreateBatchResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.Batch); err != nil { + return CreateBatchResponse{}, err + } + return result, nil +} + +// DeleteFile - Delete a previously uploaded file. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-07-01-preview +// - fileID - The ID of the file to delete. +// - options - DeleteFileOptions contains the optional parameters for the Client.DeleteFile method. +func (client *Client) DeleteFile(ctx context.Context, fileID string, options *DeleteFileOptions) (DeleteFileResponse, error) { + var err error + req, err := client.deleteFileCreateRequest(ctx, fileID, options) + if err != nil { + return DeleteFileResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return DeleteFileResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK) { + err = client.newError(httpResp) + return DeleteFileResponse{}, err + } + resp, err := client.deleteFileHandleResponse(httpResp) + return resp, err +} + +// deleteFileCreateRequest creates the DeleteFile request. +func (client *Client) deleteFileCreateRequest(ctx context.Context, fileID string, options *DeleteFileOptions) (*policy.Request, error) { + urlPath := "/files/{fileId}" + if fileID == "" { + return nil, errors.New("parameter fileID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{fileId}", url.PathEscape(fileID)) + req, err := runtime.NewRequest(ctx, http.MethodDelete, client.formatURL(urlPath, nil)) + if err != nil { + return nil, err + } + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// deleteFileHandleResponse handles the DeleteFile response. +func (client *Client) deleteFileHandleResponse(resp *http.Response) (DeleteFileResponse, error) { + result := DeleteFileResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.FileDeletionStatus); err != nil { + return DeleteFileResponse{}, err + } + return result, nil +} + // GenerateSpeechFromText - Generates text-to-speech audio from the input text. // If the operation fails it returns an *azcore.ResponseError type. // -// Generated from API version 2024-05-01-preview +// Generated from API version 2024-07-01-preview +// - body - A representation of the request options that control the behavior of a text-to-speech operation. // - options - GenerateSpeechFromTextOptions contains the optional parameters for the Client.GenerateSpeechFromText method. func (client *Client) GenerateSpeechFromText(ctx context.Context, body SpeechGenerationOptions, options *GenerateSpeechFromTextOptions) (GenerateSpeechFromTextResponse, error) { var err error @@ -55,7 +204,7 @@ func (client *Client) generateSpeechFromTextCreateRequest(ctx context.Context, b return nil, err } reqQP := req.Raw().URL.Query() - reqQP.Set("api-version", "2024-05-01-preview") + reqQP.Set("api-version", "2024-07-01-preview") req.Raw().URL.RawQuery = reqQP.Encode() runtime.SkipBodyDownload(req) req.Raw().Header["Accept"] = []string{"application/octet-stream, application/json"} @@ -69,7 +218,7 @@ func (client *Client) generateSpeechFromTextCreateRequest(ctx context.Context, b // 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 2024-05-01-preview +// Generated from API version 2024-07-01-preview // - deploymentID - Specifies either the model deployment name (when using Azure OpenAI) or model name (when using non-Azure // OpenAI) to use for this request. // - file - The audio data to transcribe. This must be the binary content of a file in one of the supported media formats: flac, @@ -102,7 +251,7 @@ func (client *Client) getAudioTranscriptionInternalCreateRequest(ctx context.Con return nil, err } reqQP := req.Raw().URL.Query() - reqQP.Set("api-version", "2024-05-01-preview") + reqQP.Set("api-version", "2024-07-01-preview") req.Raw().URL.RawQuery = reqQP.Encode() req.Raw().Header["Accept"] = []string{"application/json"} if err := setMultipartFormData(req, file, *body); err != nil { @@ -124,7 +273,7 @@ func (client *Client) getAudioTranscriptionInternalHandleResponse(resp *http.Res // data. // If the operation fails it returns an *azcore.ResponseError type. // -// Generated from API version 2024-05-01-preview +// Generated from API version 2024-07-01-preview // - deploymentID - Specifies either the model deployment name (when using Azure OpenAI) or model name (when using non-Azure // OpenAI) to use for this request. // - file - The audio data to translate. This must be the binary content of a file in one of the supported media formats: flac, @@ -157,7 +306,7 @@ func (client *Client) getAudioTranslationInternalCreateRequest(ctx context.Conte return nil, err } reqQP := req.Raw().URL.Query() - reqQP.Set("api-version", "2024-05-01-preview") + reqQP.Set("api-version", "2024-07-01-preview") req.Raw().URL.RawQuery = reqQP.Encode() req.Raw().Header["Accept"] = []string{"application/json"} if err := setMultipartFormData(req, file, *body); err != nil { @@ -175,11 +324,61 @@ func (client *Client) getAudioTranslationInternalHandleResponse(resp *http.Respo return result, nil } +// GetBatch - Gets details for a single batch specified by the given batchID. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-07-01-preview +// - batchID - The identifier of the batch. +// - options - GetBatchOptions contains the optional parameters for the Client.GetBatch method. +func (client *Client) GetBatch(ctx context.Context, batchID string, options *GetBatchOptions) (GetBatchResponse, error) { + var err error + req, err := client.getBatchCreateRequest(ctx, batchID, options) + if err != nil { + return GetBatchResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return GetBatchResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK) { + err = client.newError(httpResp) + return GetBatchResponse{}, err + } + resp, err := client.getBatchHandleResponse(httpResp) + return resp, err +} + +// getBatchCreateRequest creates the GetBatch request. +func (client *Client) getBatchCreateRequest(ctx context.Context, batchID string, options *GetBatchOptions) (*policy.Request, error) { + urlPath := "/batches/{batchId}" + if batchID == "" { + return nil, errors.New("parameter batchID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{batchId}", url.PathEscape(batchID)) + req, err := runtime.NewRequest(ctx, http.MethodGet, client.formatURL(urlPath, nil)) + if err != nil { + return nil, err + } + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// getBatchHandleResponse handles the GetBatch response. +func (client *Client) getBatchHandleResponse(resp *http.Response) (GetBatchResponse, error) { + result := GetBatchResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.Batch); err != nil { + return GetBatchResponse{}, 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 2024-05-01-preview +// Generated from API version 2024-07-01-preview +// - body - The configuration information for a chat completions request. Completions support a wide variety of tasks and generate +// text that continues from or "completes" provided prompt data. // - 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 @@ -207,7 +406,7 @@ func (client *Client) getChatCompletionsCreateRequest(ctx context.Context, body return nil, err } reqQP := req.Raw().URL.Query() - reqQP.Set("api-version", "2024-05-01-preview") + reqQP.Set("api-version", "2024-07-01-preview") req.Raw().URL.RawQuery = reqQP.Encode() req.Raw().Header["Accept"] = []string{"application/json"} if err := runtime.MarshalAsJSON(req, body); err != nil { @@ -229,7 +428,9 @@ func (client *Client) getChatCompletionsHandleResponse(resp *http.Response) (Get // text that continues from or "completes" provided prompt data. // If the operation fails it returns an *azcore.ResponseError type. // -// Generated from API version 2024-05-01-preview +// Generated from API version 2024-07-01-preview +// - body - The configuration information for a completions request. Completions support a wide variety of tasks and generate +// text that continues from or "completes" provided prompt data. // - 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 @@ -257,7 +458,7 @@ func (client *Client) getCompletionsCreateRequest(ctx context.Context, body Comp return nil, err } reqQP := req.Raw().URL.Query() - reqQP.Set("api-version", "2024-05-01-preview") + reqQP.Set("api-version", "2024-07-01-preview") req.Raw().URL.RawQuery = reqQP.Encode() req.Raw().Header["Accept"] = []string{"application/json"} if err := runtime.MarshalAsJSON(req, body); err != nil { @@ -278,7 +479,9 @@ 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 2024-05-01-preview +// Generated from API version 2024-07-01-preview +// - body - The configuration information for an embeddings request. Embeddings measure the relatedness of text strings and +// are commonly used for search, clustering, recommendations, and other similar scenarios. // - 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 @@ -306,7 +509,7 @@ func (client *Client) getEmbeddingsCreateRequest(ctx context.Context, body Embed return nil, err } reqQP := req.Raw().URL.Query() - reqQP.Set("api-version", "2024-05-01-preview") + reqQP.Set("api-version", "2024-07-01-preview") req.Raw().URL.RawQuery = reqQP.Encode() req.Raw().Header["Accept"] = []string{"application/json"} if err := runtime.MarshalAsJSON(req, body); err != nil { @@ -324,10 +527,107 @@ func (client *Client) getEmbeddingsHandleResponse(resp *http.Response) (GetEmbed return result, nil } +// GetFile - Returns information about a specific file. Does not retrieve file content. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-07-01-preview +// - fileID - The ID of the file to retrieve. +// - options - GetFileOptions contains the optional parameters for the Client.GetFile method. +func (client *Client) GetFile(ctx context.Context, fileID string, options *GetFileOptions) (GetFileResponse, error) { + var err error + req, err := client.getFileCreateRequest(ctx, fileID, options) + if err != nil { + return GetFileResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return GetFileResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK) { + err = client.newError(httpResp) + return GetFileResponse{}, err + } + resp, err := client.getFileHandleResponse(httpResp) + return resp, err +} + +// getFileCreateRequest creates the GetFile request. +func (client *Client) getFileCreateRequest(ctx context.Context, fileID string, options *GetFileOptions) (*policy.Request, error) { + urlPath := "/files/{fileId}" + if fileID == "" { + return nil, errors.New("parameter fileID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{fileId}", url.PathEscape(fileID)) + req, err := runtime.NewRequest(ctx, http.MethodGet, client.formatURL(urlPath, nil)) + if err != nil { + return nil, err + } + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// getFileHandleResponse handles the GetFile response. +func (client *Client) getFileHandleResponse(resp *http.Response) (GetFileResponse, error) { + result := GetFileResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.File); err != nil { + return GetFileResponse{}, err + } + return result, nil +} + +// GetFileContent - Returns information about a specific file. Does not retrieve file content. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-07-01-preview +// - fileID - The ID of the file to retrieve. +// - options - GetFileContentOptions contains the optional parameters for the Client.GetFileContent method. +func (client *Client) GetFileContent(ctx context.Context, fileID string, options *GetFileContentOptions) (GetFileContentResponse, error) { + var err error + req, err := client.getFileContentCreateRequest(ctx, fileID, options) + if err != nil { + return GetFileContentResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return GetFileContentResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK) { + err = client.newError(httpResp) + return GetFileContentResponse{}, err + } + resp, err := client.getFileContentHandleResponse(httpResp) + return resp, err +} + +// getFileContentCreateRequest creates the GetFileContent request. +func (client *Client) getFileContentCreateRequest(ctx context.Context, fileID string, options *GetFileContentOptions) (*policy.Request, error) { + urlPath := "/files/{fileId}/content" + if fileID == "" { + return nil, errors.New("parameter fileID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{fileId}", url.PathEscape(fileID)) + req, err := runtime.NewRequest(ctx, http.MethodGet, client.formatURL(urlPath, nil)) + if err != nil { + return nil, err + } + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// getFileContentHandleResponse handles the GetFileContent response. +func (client *Client) getFileContentHandleResponse(resp *http.Response) (GetFileContentResponse, error) { + result := GetFileContentResponse{} + if err := runtime.UnmarshalAsByteArray(resp, &result.Value, runtime.Base64StdFormat); err != nil { + return GetFileContentResponse{}, err + } + return result, nil +} + // GetImageGenerations - Creates an image given a prompt. // If the operation fails it returns an *azcore.ResponseError type. // -// Generated from API version 2024-05-01-preview +// Generated from API version 2024-07-01-preview +// - body - Represents the request data used to generate images. // - options - GetImageGenerationsOptions contains the optional parameters for the Client.GetImageGenerations method. func (client *Client) GetImageGenerations(ctx context.Context, body ImageGenerationOptions, options *GetImageGenerationsOptions) (GetImageGenerationsResponse, error) { var err error @@ -355,7 +655,7 @@ func (client *Client) getImageGenerationsCreateRequest(ctx context.Context, body return nil, err } reqQP := req.Raw().URL.Query() - reqQP.Set("api-version", "2024-05-01-preview") + reqQP.Set("api-version", "2024-07-01-preview") req.Raw().URL.RawQuery = reqQP.Encode() req.Raw().Header["Accept"] = []string{"application/json"} if err := runtime.MarshalAsJSON(req, body); err != nil { @@ -372,3 +672,136 @@ func (client *Client) getImageGenerationsHandleResponse(resp *http.Response) (Ge } return result, nil } + +// listBatches - Gets a list of all batches owned by the Azure OpenAI resource. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-07-01-preview +// - options - ListBatchesOptions contains the optional parameters for the Client.listBatches method. +func (client *Client) listBatches(ctx context.Context, options *ListBatchesOptions) (ListBatchesResponse, error) { + var err error + req, err := client.listBatchesCreateRequest(ctx, options) + if err != nil { + return ListBatchesResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return ListBatchesResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK) { + err = client.newError(httpResp) + return ListBatchesResponse{}, err + } + resp, err := client.listBatchesHandleResponse(httpResp) + return resp, err +} + +// listBatchesCreateRequest creates the listBatches request. +func (client *Client) listBatchesCreateRequest(ctx context.Context, options *ListBatchesOptions) (*policy.Request, error) { + urlPath := "/batches" + req, err := runtime.NewRequest(ctx, http.MethodGet, client.formatURL(urlPath, nil)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + if options != nil && options.After != nil { + reqQP.Set("after", *options.After) + } + if options != nil && options.Limit != nil { + reqQP.Set("limit", strconv.FormatInt(int64(*options.Limit), 10)) + } + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// listBatchesHandleResponse handles the listBatches response. +func (client *Client) listBatchesHandleResponse(resp *http.Response) (ListBatchesResponse, error) { + result := ListBatchesResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.ListBatchesPage); err != nil { + return ListBatchesResponse{}, err + } + return result, nil +} + +// ListFiles - Gets a list of previously uploaded files. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-07-01-preview +// - options - ListFilesOptions contains the optional parameters for the Client.ListFiles method. +func (client *Client) ListFiles(ctx context.Context, options *ListFilesOptions) (ListFilesResponse, error) { + var err error + req, err := client.listFilesCreateRequest(ctx, options) + if err != nil { + return ListFilesResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return ListFilesResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK) { + err = client.newError(httpResp) + return ListFilesResponse{}, err + } + resp, err := client.listFilesHandleResponse(httpResp) + return resp, err +} + +// listFilesCreateRequest creates the ListFiles request. +func (client *Client) listFilesCreateRequest(ctx context.Context, options *ListFilesOptions) (*policy.Request, error) { + urlPath := "/files" + req, err := runtime.NewRequest(ctx, http.MethodGet, client.formatURL(urlPath, nil)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + if options != nil && options.Purpose != nil { + reqQP.Set("purpose", string(*options.Purpose)) + } + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// listFilesHandleResponse handles the ListFiles response. +func (client *Client) listFilesHandleResponse(resp *http.Response) (ListFilesResponse, error) { + result := ListFilesResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.FileListResponse); err != nil { + return ListFilesResponse{}, err + } + return result, nil +} + +// UploadFile - Uploads a file for use by other operations. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-07-01-preview +// - file - The file data (not filename) to upload. +// - purpose - The intended purpose of the file. +// - options - UploadFileOptions contains the optional parameters for the Client.UploadFile method. +func (client *Client) UploadFile(ctx context.Context, file io.ReadSeekCloser, purpose FilePurpose, options *UploadFileOptions) (UploadFileResponse, error) { + var err error + req, err := client.uploadFileCreateRequest(ctx, file, purpose, options) + if err != nil { + return UploadFileResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return UploadFileResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK) { + err = client.newError(httpResp) + return UploadFileResponse{}, err + } + resp, err := client.uploadFileHandleResponse(httpResp) + return resp, err +} + +// uploadFileHandleResponse handles the UploadFile response. +func (client *Client) uploadFileHandleResponse(resp *http.Response) (UploadFileResponse, error) { + result := UploadFileResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.File); err != nil { + return UploadFileResponse{}, err + } + return result, nil +} diff --git a/sdk/ai/azopenai/client_batch_files_test.go b/sdk/ai/azopenai/client_batch_files_test.go new file mode 100644 index 0000000000..95aaeda170 --- /dev/null +++ b/sdk/ai/azopenai/client_batch_files_test.go @@ -0,0 +1,85 @@ +//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 ( + "bytes" + "context" + "testing" + + "github.com/Azure/azure-sdk-for-go/sdk/ai/azopenai" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming" + "github.com/Azure/azure-sdk-for-go/sdk/internal/recording" + "github.com/stretchr/testify/require" +) + +func TestFilesOperations(t *testing.T) { + if recording.GetRecordMode() != recording.LiveMode { + t.Skip("Skipping test in playback and record mode") + } + + client := newTestClient(t, azureOpenAI.Files.Endpoint) + + uploadResp, err := client.UploadFile(context.Background(), streaming.NopCloser(bytes.NewReader([]byte("hello world"))), azopenai.FilePurposeAssistants, nil) + require.NoError(t, err) + + t.Cleanup(func() { + _, err := client.DeleteFile(context.Background(), *uploadResp.ID, nil) + require.NoError(t, err) + }) + + getFileResp, err := client.GetFile(context.Background(), *uploadResp.ID, nil) + require.NoError(t, err) + + require.Equal(t, azopenai.FilePurposeAssistants, *getFileResp.Purpose) + + // fileContentsResp, err := client.GetFileContent(context.Background(), *getFileResp.ID, nil) + // require.NoError(t, err) + // require.NotEmpty(t, fileContentsResp.Value) + + filesResp, err := client.ListFiles(context.Background(), nil) + require.NoError(t, err) + require.NotEmpty(t, filesResp.Data) +} + +func TestFileDownload(t *testing.T) { + t.Skip("Need to find a file type we can download") +} + +func TestBatchOperations(t *testing.T) { + if recording.GetRecordMode() != recording.LiveMode { + t.Skip("Skipping test in playback and record mode") + } + client := newTestClient(t, azureOpenAI.Files.Endpoint) + + // TODO: this is a little tricky because the files aren't instantly uploaded, so we can't just proceed + // with the rest of the test. + + // uploadResp, err := client.UploadFile(context.Background(), streaming.NopCloser(bytes.NewReader([]byte("{}"))), azopenai.FilePurposeBatch, &azopenai.UploadFileOptions{ + // Filename: to.Ptr("file.jsonl"), + // }) + // require.NoError(t, err) + + // t.Cleanup(func() { + // _, err := client.DeleteFile(context.Background(), *uploadResp.ID, nil) + // require.NoError(t, err) + // }) + + // createResp, err := client.CreateBatch(context.Background(), azopenai.BatchCreateRequest{ + // InputFileID: uploadResp.ID, + // }, nil) + // require.NoError(t, err) + // require.NotEmpty(t, createResp) + + batchPager := client.NewListBatchesPager(nil) + + for batchPager.More() { + resp, err := batchPager.NextPage(context.Background()) + require.NoError(t, err) + require.NotEmpty(t, resp) + } +} diff --git a/sdk/ai/azopenai/client_custom_files.go b/sdk/ai/azopenai/client_custom_files.go new file mode 100644 index 0000000000..d170aefe71 --- /dev/null +++ b/sdk/ai/azopenai/client_custom_files.go @@ -0,0 +1,121 @@ +//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" + "net/textproto" + "path/filepath" + "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" +) + +func (client *Client) uploadFileCreateRequest(ctx context.Context, file io.ReadSeeker, purpose FilePurpose, options *UploadFileOptions) (*policy.Request, error) { + urlPath := client.formatURL("/files", nil) + req, err := runtime.NewRequest(ctx, http.MethodPost, urlPath) + if err != nil { + return nil, err + } + req.Raw().Header["Accept"] = []string{"application/json"} + fileName := "" + + if options != nil && options.Filename != nil { + fileName = *options.Filename + } + + fileBytes, err := io.ReadAll(file) + + if err != nil { + return nil, err + } + + if err := writeMultipart(req, fileBytes, fileName, purpose); err != nil { + return nil, err + } + + return req, nil +} + +func writeMultipart(req *policy.Request, fileContents []byte, filename string, purpose FilePurpose) error { + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + + fileWriter, err := createFormFile(writer, "file", filename) + + if err != nil { + return err + } + + if _, err := fileWriter.Write(fileContents); err != nil { + return err + } + + if err := writer.WriteField("purpose", string(purpose)); err != nil { + return err + } + + if err := writer.Close(); err != nil { + return err + } + + return req.SetBody(streaming.NopCloser(bytes.NewReader(body.Bytes())), writer.FormDataContentType()) +} + +func createFormFile(w *multipart.Writer, fieldname, filename string) (io.Writer, error) { + h := make(textproto.MIMEHeader) + h.Set("Content-Disposition", + fmt.Sprintf(`form-data; name="%s"; filename="%s"`, + quoteReplacer.Replace(fieldname), quoteReplacer.Replace(filename))) + + contentType := openAIMimeTypes[filepath.Ext(filename)] + + if contentType == "" { + contentType = "application/octet-stream" + } + + h.Set("Content-Type", contentType) + return w.CreatePart(h) +} + +var openAIMimeTypes = map[string]string{ + ".c": "text/x-c", + ".cpp": "text/x-c++", + ".csv": "application/csv", + ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ".html": "text/html", + ".java": "text/x-java", + ".json": "application/json", + ".md": "text/markdown", + ".pdf": "application/pdf", + ".php": "text/x-php", + ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation", + ".py": "text/x-python", + ".rb": "text/x-ruby", + ".tex": "text/x-tex", + ".txt": "text/plain", + ".css": "text/css", + ".jpeg": "image/jpeg", + ".jpg": "image/jpeg", + ".js": "text/javascript", + ".gif": "image/gif", + ".png": "image/png", + ".tar": "application/x-tar", + ".ts": "application/typescript", + ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + ".xml": "application/xml", + ".zip": "application/z", +} + +var quoteReplacer = strings.NewReplacer("\\", "\\\\", `"`, "\\\"") diff --git a/sdk/ai/azopenai/client_functions_test.go b/sdk/ai/azopenai/client_functions_test.go index 0753c1cba3..6d37b7100c 100644 --- a/sdk/ai/azopenai/client_functions_test.go +++ b/sdk/ai/azopenai/client_functions_test.go @@ -15,24 +15,28 @@ import ( "github.com/stretchr/testify/require" ) -type Params struct { - Type string `json:"type"` - Properties map[string]ParamProperty `json:"properties"` - Required []string `json:"required,omitempty"` -} - -type ParamProperty struct { - Type string `json:"type"` - Description string `json:"description,omitempty"` - Enum []string `json:"enum,omitempty"` -} - func TestGetChatCompletions_usingFunctions(t *testing.T) { if recording.GetRecordMode() == recording.PlaybackMode { t.Skip("https://github.com/Azure/azure-sdk-for-go/issues/22869") } // https://platform.openai.com/docs/guides/gpt/function-calling + parametersJSON, err := json.Marshal(map[string]any{ + "required": []string{"location"}, + "type": "object", + "properties": map[string]any{ + "location": map[string]any{ + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + }, + "unit": map[string]any{ + "type": "string", + "enum": []string{"celsius", "fahrenheit"}, + }, + }, + }) + require.NoError(t, err) + testFn := func(t *testing.T, chatClient *azopenai.Client, deploymentName string, toolChoice *azopenai.ChatCompletionsToolChoice) { body := azopenai.ChatCompletionsOptions{ DeploymentName: &deploymentName, @@ -46,20 +50,7 @@ func TestGetChatCompletions_usingFunctions(t *testing.T) { Function: &azopenai.FunctionDefinition{ Name: to.Ptr("get_current_weather"), Description: to.Ptr("Get the current weather in a given location"), - Parameters: Params{ - Required: []string{"location"}, - Type: "object", - Properties: map[string]ParamProperty{ - "location": { - Type: "string", - Description: "The city and state, e.g. San Francisco, CA", - }, - "unit": { - Type: "string", - Enum: []string{"celsius", "fahrenheit"}, - }, - }, - }, + Parameters: parametersJSON, }, }, }, @@ -134,6 +125,23 @@ func TestGetChatCompletions_usingFunctions_legacy(t *testing.T) { if recording.GetRecordMode() == recording.PlaybackMode { t.Skip("https://github.com/Azure/azure-sdk-for-go/issues/22869") } + + parametersJSON, err := json.Marshal(map[string]any{ + "required": []string{"location"}, + "type": "object", + "properties": map[string]any{ + "location": map[string]any{ + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + }, + "unit": map[string]any{ + "type": "string", + "enum": []string{"celsius", "fahrenheit"}, + }, + }, + }) + require.NoError(t, err) + testFn := func(t *testing.T, epm endpointWithModel) { client := newTestClient(t, epm.Endpoint) @@ -151,20 +159,7 @@ func TestGetChatCompletions_usingFunctions_legacy(t *testing.T) { { Name: to.Ptr("get_current_weather"), Description: to.Ptr("Get the current weather in a given location"), - Parameters: Params{ - Required: []string{"location"}, - Type: "object", - Properties: map[string]ParamProperty{ - "location": { - Type: "string", - Description: "The city and state, e.g. San Francisco, CA", - }, - "unit": { - Type: "string", - Enum: []string{"celsius", "fahrenheit"}, - }, - }, - }, + Parameters: parametersJSON, }, }, Temperature: to.Ptr[float32](0.0), @@ -204,6 +199,22 @@ func TestGetChatCompletions_usingFunctions_legacy(t *testing.T) { } func TestGetChatCompletions_usingFunctions_streaming(t *testing.T) { + parametersJSON, err := json.Marshal(map[string]any{ + "required": []string{"location"}, + "type": "object", + "properties": map[string]any{ + "location": map[string]any{ + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + }, + "unit": map[string]any{ + "type": "string", + "enum": []string{"celsius", "fahrenheit"}, + }, + }, + }) + require.NoError(t, err) + testFn := func(t *testing.T, epm endpointWithModel) { body := azopenai.ChatCompletionsOptions{ DeploymentName: &epm.Model, @@ -217,20 +228,7 @@ func TestGetChatCompletions_usingFunctions_streaming(t *testing.T) { Function: &azopenai.FunctionDefinition{ Name: to.Ptr("get_current_weather"), Description: to.Ptr("Get the current weather in a given location"), - Parameters: Params{ - Required: []string{"location"}, - Type: "object", - Properties: map[string]ParamProperty{ - "location": { - Type: "string", - Description: "The city and state, e.g. San Francisco, CA", - }, - "unit": { - Type: "string", - Enum: []string{"celsius", "fahrenheit"}, - }, - }, - }, + Parameters: parametersJSON, }, }, }, diff --git a/sdk/ai/azopenai/client_shared_test.go b/sdk/ai/azopenai/client_shared_test.go index 5412e514ac..6777d9165f 100644 --- a/sdk/ai/azopenai/client_shared_test.go +++ b/sdk/ai/azopenai/client_shared_test.go @@ -32,6 +32,7 @@ type endpoint struct { } type testVars struct { + Batch endpointWithModel ChatCompletions endpointWithModel ChatCompletionsLegacyFunctions endpointWithModel ChatCompletionsOYD endpointWithModel // azure only @@ -41,6 +42,7 @@ type testVars struct { Completions endpointWithModel DallE endpointWithModel Embeddings endpointWithModel + Files endpointWithModel Speech endpointWithModel TextEmbedding3Small endpointWithModel Vision endpointWithModel @@ -112,6 +114,10 @@ var azureOpenAI, openAI = func() (testVars, testVars) { newTestVarsFn := func(azure bool) testVars { return testVars{ + Batch: endpointWithModel{ + Endpoint: ifAzure(azure, servers.SWECentral, servers.OpenAI), + Model: "", + }, ChatCompletions: endpointWithModel{ Endpoint: ifAzure(azure, servers.USEast, servers.OpenAI), Model: ifAzure(azure, "gpt-4-0613", "gpt-4-0613"), @@ -144,6 +150,10 @@ var azureOpenAI, openAI = func() (testVars, testVars) { Endpoint: ifAzure(azure, servers.USEast, servers.OpenAI), Model: ifAzure(azure, "text-embedding-ada-002", "text-embedding-ada-002"), }, + Files: endpointWithModel{ + Endpoint: ifAzure(azure, servers.SWECentral, servers.OpenAI), + Model: "", + }, Speech: endpointWithModel{ Endpoint: ifAzure(azure, servers.USEast, servers.OpenAI), Model: ifAzure(azure, "tts-1", "tts-1"), diff --git a/sdk/ai/azopenai/constants.go b/sdk/ai/azopenai/constants.go index 235ea5d240..61d91245fe 100644 --- a/sdk/ai/azopenai/constants.go +++ b/sdk/ai/azopenai/constants.go @@ -187,6 +187,42 @@ func PossibleAzureSearchQueryTypeValues() []AzureSearchQueryType { } } +// BatchStatus - The status of a batch. +type BatchStatus string + +const ( + // BatchStatusCancelled - The batch was cancelled. + BatchStatusCancelled BatchStatus = "cancelled" + // BatchStatusCancelling - Cancellation of the batch has been initiated. + BatchStatusCancelling BatchStatus = "cancelling" + // BatchStatusCompleted - The batch has been completed and the results are ready. + BatchStatusCompleted BatchStatus = "completed" + // BatchStatusExpired - The batch was not able to complete within the 24-hour time window. + BatchStatusExpired BatchStatus = "expired" + // BatchStatusFailed - The input file has failed the validation process. + BatchStatusFailed BatchStatus = "failed" + // BatchStatusFinalizing - The batch has completed and the results are being prepared. + BatchStatusFinalizing BatchStatus = "finalizing" + // BatchStatusInProgress - The input file was successfully validated and the batch is currently being executed. + BatchStatusInProgress BatchStatus = "in_progress" + // BatchStatusValidating - The input file is being validated before the batch can begin. + BatchStatusValidating BatchStatus = "validating" +) + +// PossibleBatchStatusValues returns the possible values for the BatchStatus const type. +func PossibleBatchStatusValues() []BatchStatus { + return []BatchStatus{ + BatchStatusCancelled, + BatchStatusCancelling, + BatchStatusCompleted, + BatchStatusExpired, + BatchStatusFailed, + BatchStatusFinalizing, + BatchStatusInProgress, + BatchStatusValidating, + } +} + // ChatCompletionRequestMessageContentPartImageURLDetail - Specifies the detail level of the image. Learn more in the Vision // guide [/docs/guides/vision/low-or-high-fidelity-image-understanding]. type ChatCompletionRequestMessageContentPartImageURLDetail string @@ -236,6 +272,8 @@ const ( // provide a standard chat // completions response. Response content may still be influenced by the provided tool definitions. ChatCompletionsToolSelectionPresetNone ChatCompletionsToolSelectionPreset = "none" + // ChatCompletionsToolSelectionPresetRequired - Specifies that the model must call one or more tools. + ChatCompletionsToolSelectionPresetRequired ChatCompletionsToolSelectionPreset = "required" ) // PossibleChatCompletionsToolSelectionPresetValues returns the possible values for the ChatCompletionsToolSelectionPreset const type. @@ -243,6 +281,7 @@ func PossibleChatCompletionsToolSelectionPresetValues() []ChatCompletionsToolSel return []ChatCompletionsToolSelectionPreset{ ChatCompletionsToolSelectionPresetAuto, ChatCompletionsToolSelectionPresetNone, + ChatCompletionsToolSelectionPresetRequired, } } @@ -399,6 +438,81 @@ func PossibleEmbeddingEncodingFormatValues() []EmbeddingEncodingFormat { } } +// FilePurpose - The possible values denoting the intended usage of a file. +type FilePurpose string + +const ( + // FilePurposeAssistants - Indicates a file is used as input to assistants. + FilePurposeAssistants FilePurpose = "assistants" + // FilePurposeAssistantsOutput - Indicates a file is used as output by assistants. + FilePurposeAssistantsOutput FilePurpose = "assistants_output" + // FilePurposeBatch - Indicates a file is used as input to . + FilePurposeBatch FilePurpose = "batch" + // FilePurposeBatchOutput - Indicates a file is used as output by a vector store batch operation. + FilePurposeBatchOutput FilePurpose = "batch_output" + // FilePurposeFineTune - Indicates a file is used for fine tuning input. + FilePurposeFineTune FilePurpose = "fine-tune" + // FilePurposeFineTuneResults - Indicates a file is used for fine tuning results. + FilePurposeFineTuneResults FilePurpose = "fine-tune-results" + // FilePurposeVision - Indicates a file is used as input to a vision operation. + FilePurposeVision FilePurpose = "vision" +) + +// PossibleFilePurposeValues returns the possible values for the FilePurpose const type. +func PossibleFilePurposeValues() []FilePurpose { + return []FilePurpose{ + FilePurposeAssistants, + FilePurposeAssistantsOutput, + FilePurposeBatch, + FilePurposeBatchOutput, + FilePurposeFineTune, + FilePurposeFineTuneResults, + FilePurposeVision, + } +} + +// FileState - The state of the file. +type FileState string + +const ( + // FileStateDeleted - The entity has been deleted but may still be referenced by other entities predating the deletion. It + // can be categorized as a + // terminal state. + FileStateDeleted FileState = "deleted" + // FileStateDeleting - The entity is in the process to be deleted. This state is not returned by Azure OpenAI and exposed + // only for compatibility. + // It can be categorized as an active state. + FileStateDeleting FileState = "deleting" + // FileStateError - The operation has completed processing with a failure and cannot be further consumed. It can be categorized + // as a terminal state. + FileStateError FileState = "error" + // FileStatePending - The operation was created and is not queued to be processed in the future. It can be categorized as + // an inactive state. + FileStatePending FileState = "pending" + // FileStateProcessed - The operation has successfully processed and is ready for consumption. It can be categorized as a + // terminal state. + FileStateProcessed FileState = "processed" + // FileStateRunning - The operation has started to be processed. It can be categorized as an active state. + FileStateRunning FileState = "running" + // FileStateUploaded - The file has been uploaded but it's not yet processed. This state is not returned by Azure OpenAI and + // exposed only for + // compatibility. It can be categorized as an inactive state. + FileStateUploaded FileState = "uploaded" +) + +// PossibleFileStateValues returns the possible values for the FileState const type. +func PossibleFileStateValues() []FileState { + return []FileState{ + FileStateDeleted, + FileStateDeleting, + FileStateError, + FileStatePending, + FileStateProcessed, + FileStateRunning, + FileStateUploaded, + } +} + // FunctionCallPreset - The collection of predefined behaviors for handling request-provided function information in a chat // completions operation. type FunctionCallPreset string diff --git a/sdk/ai/azopenai/custom_client.go b/sdk/ai/azopenai/custom_client.go index 5d07341352..a7e7e4897b 100644 --- a/sdk/ai/azopenai/custom_client.go +++ b/sdk/ai/azopenai/custom_client.go @@ -49,7 +49,7 @@ func NewClient(endpoint string, credential azcore.TokenCredential, options *Clie }) azcoreClient, err := azcore.NewClient(clientName, version, runtime.PipelineOptions{ - PerRetry: []policy.Policy{authPolicy}, + PerRetry: []policy.Policy{authPolicy, tempAPIVersionPolicy{}}, }, &options.ClientOptions) if err != nil { @@ -79,7 +79,7 @@ func NewClientWithKeyCredential(endpoint string, credential *azcore.KeyCredentia }) azcoreClient, err := azcore.NewClient(clientName, version, runtime.PipelineOptions{ - PerRetry: []policy.Policy{authPolicy}, + PerRetry: []policy.Policy{authPolicy, tempAPIVersionPolicy{}}, }, &options.ClientOptions) if err != nil { return nil, err @@ -238,15 +238,19 @@ func (client *Client) GetChatCompletionsStream(ctx context.Context, body ChatCom }, nil } -func (client *Client) formatURL(path string, deploymentID string) string { +func (client *Client) formatURL(path string, deployment *string) string { switch path { // https://learn.microsoft.com/en-us/azure/cognitive-services/openai/reference#image-generation case "/images/generations:submit": - return runtime.JoinPaths(client.endpoint, "openai", path) + return runtime.JoinPaths(client.endpoint, path) default: if client.azure { - escapedDeplID := url.PathEscape(deploymentID) - return runtime.JoinPaths(client.endpoint, "openai", "deployments", escapedDeplID, path) + if deployment != nil { + escapedDeplID := url.PathEscape(*deployment) + return runtime.JoinPaths(client.endpoint, "openai", "deployments", escapedDeplID, path) + } else { + return runtime.JoinPaths(client.endpoint, "openai", path) + } } return runtime.JoinPaths(client.endpoint, path) @@ -262,35 +266,27 @@ type clientData struct { azure bool } -func getDeployment[T SpeechGenerationOptions | AudioTranscriptionOptions | AudioTranslationOptions | ChatCompletionsOptions | CompletionsOptions | EmbeddingsOptions | *getAudioTranscriptionInternalOptions | *getAudioTranslationInternalOptions | ImageGenerationOptions](v T) string { - var p *string - +func getDeployment[T SpeechGenerationOptions | AudioTranscriptionOptions | AudioTranslationOptions | ChatCompletionsOptions | CompletionsOptions | EmbeddingsOptions | *getAudioTranscriptionInternalOptions | *getAudioTranslationInternalOptions | ImageGenerationOptions](v T) *string { switch a := any(v).(type) { - case SpeechGenerationOptions: - p = a.DeploymentName case AudioTranscriptionOptions: - p = a.DeploymentName + return a.DeploymentName case AudioTranslationOptions: - p = a.DeploymentName + return a.DeploymentName case ChatCompletionsOptions: - p = a.DeploymentName + return a.DeploymentName case CompletionsOptions: - p = a.DeploymentName + return a.DeploymentName case EmbeddingsOptions: - p = a.DeploymentName + return a.DeploymentName case *getAudioTranscriptionInternalOptions: - p = a.DeploymentName + return a.DeploymentName case *getAudioTranslationInternalOptions: - p = a.DeploymentName + return a.DeploymentName case ImageGenerationOptions: - p = a.DeploymentName + return a.DeploymentName } - if p != nil { - return *p - } - - return "" + return nil } // ChatRequestUserMessageContent contains the user prompt - either as a single string @@ -302,14 +298,14 @@ type ChatRequestUserMessageContent struct { } // NewChatRequestUserMessageContent creates a [azopenai.ChatRequestUserMessageContent]. -func NewChatRequestUserMessageContent[T string | []ChatCompletionRequestMessageContentPartClassification](v T) ChatRequestUserMessageContent { +func NewChatRequestUserMessageContent[T string | []ChatCompletionRequestMessageContentPartClassification](v T) *ChatRequestUserMessageContent { switch actualV := any(v).(type) { case string: - return ChatRequestUserMessageContent{value: &actualV} + return &ChatRequestUserMessageContent{value: &actualV} case []ChatCompletionRequestMessageContentPartClassification: - return ChatRequestUserMessageContent{value: actualV} + return &ChatRequestUserMessageContent{value: actualV} } - return ChatRequestUserMessageContent{} + return &ChatRequestUserMessageContent{} } // MarshalJSON implements the json.Marshaller interface for type Error. @@ -325,3 +321,13 @@ func (c *ChatRequestUserMessageContent) UnmarshalJSON(data []byte) error { func allowInsecure(options *ClientOptions) bool { return options != nil && options.InsecureAllowCredentialWithHTTP } + +// NOTE: This is a workaround for an emitter issue, see: https://github.com/Azure/azure-sdk-for-go/issues/23417 +type tempAPIVersionPolicy struct{} + +func (tavp tempAPIVersionPolicy) Do(req *policy.Request) (*http.Response, error) { + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-07-01-preview") + req.Raw().URL.RawQuery = reqQP.Encode() + return req.Next() +} diff --git a/sdk/ai/azopenai/custom_client_audio.go b/sdk/ai/azopenai/custom_client_audio.go index 558eaec727..59d93ee887 100644 --- a/sdk/ai/azopenai/custom_client_audio.go +++ b/sdk/ai/azopenai/custom_client_audio.go @@ -90,7 +90,7 @@ func (client *Client) GetAudioTranslation(ctx context.Context, body AudioTransla return GetAudioTranslationResponse(resp), nil } -func setMultipartFormData[T getAudioTranscriptionInternalOptions | getAudioTranslationInternalOptions](req *policy.Request, file io.ReadSeekCloser, options T) error { +func setMultipartFormData[T getAudioTranscriptionInternalOptions | getAudioTranslationInternalOptions | UploadFileOptions](req *policy.Request, file io.ReadSeekCloser, options T) error { body := bytes.Buffer{} writer := multipart.NewWriter(&body) diff --git a/sdk/ai/azopenai/custom_client_pagers.go b/sdk/ai/azopenai/custom_client_pagers.go new file mode 100644 index 0000000000..8f77c87870 --- /dev/null +++ b/sdk/ai/azopenai/custom_client_pagers.go @@ -0,0 +1,23 @@ +// 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 ( + "context" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" +) + +// NewListBatchesPager returns a pager for listing batches. +func (c *Client) NewListBatchesPager(options *ListBatchesOptions) *runtime.Pager[ListBatchesResponse] { + nextPageFn := func(ctx context.Context, options *ListBatchesOptions) (ListBatchesResponse, error) { + return c.listBatches(ctx, options) + } + + return newOpenAIPager(c, nextPageFn, options) +} + +func (o *ListBatchesOptions) updateAfter(after *string) { o.After = after } +func (r ListBatchesResponse) lastID() *string { return r.LastID } +func (r ListBatchesResponse) hasMore() bool { return *r.HasMore } diff --git a/sdk/ai/azopenai/custom_client_test.go b/sdk/ai/azopenai/custom_client_test.go index f6e42a411b..cbdd861c1f 100644 --- a/sdk/ai/azopenai/custom_client_test.go +++ b/sdk/ai/azopenai/custom_client_test.go @@ -10,73 +10,15 @@ import ( "context" "errors" "io" - "reflect" "strings" "testing" "github.com/Azure/azure-sdk-for-go/sdk/ai/azopenai" - "github.com/Azure/azure-sdk-for-go/sdk/azcore" "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 TestNewClient(t *testing.T) { - type args struct { - endpoint string - credential azcore.TokenCredential - options *azopenai.ClientOptions - } - tests := []struct { - name string - args args - want *azopenai.Client - wantErr bool - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := azopenai.NewClient(tt.args.endpoint, tt.args.credential, tt.args.options) - if (err != nil) != tt.wantErr { - t.Errorf("NewClient() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("NewClient() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestNewClientWithKeyCredential(t *testing.T) { - type args struct { - endpoint string - credential *azcore.KeyCredential - options *azopenai.ClientOptions - } - tests := []struct { - name string - args args - want *azopenai.Client - wantErr bool - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := azopenai.NewClientWithKeyCredential(tt.args.endpoint, tt.args.credential, tt.args.options) - if (err != nil) != tt.wantErr { - t.Errorf("NewClientWithKeyCredential() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("NewClientWithKeyCredential() = %v, want %v", got, tt.want) - } - }) - } -} - func TestGetCompletionsStream(t *testing.T) { testFn := func(t *testing.T, epm endpointWithModel) { body := azopenai.CompletionsOptions{ diff --git a/sdk/ai/azopenai/example_client_getchatcompletions_test.go b/sdk/ai/azopenai/example_client_getchatcompletions_test.go index 90b31da99b..1084fc8c09 100644 --- a/sdk/ai/azopenai/example_client_getchatcompletions_test.go +++ b/sdk/ai/azopenai/example_client_getchatcompletions_test.go @@ -131,6 +131,31 @@ func ExampleClient_GetChatCompletions_functions() { return } + jsonBytes, err := json.Marshal(map[string]any{ + "required": []string{"location"}, + "type": "object", + "properties": map[string]any{ + "location": map[string]any{ + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + }, + "unit": map[string]any{ + "type": "string", + "enum": []string{"celsius", "fahrenheit"}, + }, + }, + }) + + if err != nil { + panic(err) + } + + funcDef := &azopenai.FunctionDefinition{ + Name: to.Ptr("get_current_weather"), + Description: to.Ptr("Get the current weather in a given location"), + Parameters: jsonBytes, + } + resp, err := client.GetChatCompletions(context.TODO(), azopenai.ChatCompletionsOptions{ DeploymentName: &modelDeploymentID, Messages: []azopenai.ChatRequestMessageClassification{ @@ -140,24 +165,7 @@ func ExampleClient_GetChatCompletions_functions() { }, Tools: []azopenai.ChatCompletionsToolDefinitionClassification{ &azopenai.ChatCompletionsFunctionToolDefinition{ - Function: &azopenai.FunctionDefinition{ - Name: to.Ptr("get_current_weather"), - Description: to.Ptr("Get the current weather in a given location"), - Parameters: map[string]any{ - "required": []string{"location"}, - "type": "object", - "properties": map[string]any{ - "location": map[string]any{ - "type": "string", - "description": "The city and state, e.g. San Francisco, CA", - }, - "unit": map[string]any{ - "type": "string", - "enum": []string{"celsius", "fahrenheit"}, - }, - }, - }, - }, + Function: funcDef, }, }, Temperature: to.Ptr[float32](0.0), @@ -219,6 +227,27 @@ func ExampleClient_GetChatCompletions_legacyFunctions() { return } + parametersJSON, err := json.Marshal(map[string]any{ + "required": []string{"location"}, + "type": "object", + "properties": map[string]any{ + "location": map[string]any{ + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + }, + "unit": map[string]any{ + "type": "string", + "enum": []string{"celsius", "fahrenheit"}, + }, + }, + }) + + if err != nil { + // TODO: Update the following line with your application specific error handling logic + log.Printf("ERROR: %s", err) + return + } + resp, err := client.GetChatCompletions(context.TODO(), azopenai.ChatCompletionsOptions{ DeploymentName: &modelDeploymentID, Messages: []azopenai.ChatRequestMessageClassification{ @@ -234,20 +263,7 @@ func ExampleClient_GetChatCompletions_legacyFunctions() { Name: to.Ptr("get_current_weather"), Description: to.Ptr("Get the current weather in a given location"), - Parameters: map[string]any{ - "required": []string{"location"}, - "type": "object", - "properties": map[string]any{ - "location": map[string]any{ - "type": "string", - "description": "The city and state, e.g. San Francisco, CA", - }, - "unit": map[string]any{ - "type": "string", - "enum": []string{"celsius", "fahrenheit"}, - }, - }, - }, + Parameters: parametersJSON, }, }, Temperature: to.Ptr[float32](0.0), diff --git a/sdk/ai/azopenai/models.go b/sdk/ai/azopenai/models.go index ce9819b606..1afe9a93fd 100644 --- a/sdk/ai/azopenai/models.go +++ b/sdk/ai/azopenai/models.go @@ -618,6 +618,185 @@ type AzureSearchIndexFieldMappingOptions struct { VectorFields []string } +// Batch - The Batch object. +type Batch struct { + // REQUIRED; The id assigned to the Batch. + ID *string + + // REQUIRED; The ID of the input file for the batch. + InputFileID *string + + // REQUIRED; The object type, which is always batch. + Object *string + + // The Unix timestamp (in seconds) for when the batch was cancelled. + CancelledAt *time.Time + + // The Unix timestamp (in seconds) for when the batch started cancelling. + CancellingAt *time.Time + + // The Unix timestamp (in seconds) for when the batch was completed. + CompletedAt *time.Time + + // The time frame within which the batch should be processed. + CompletionWindow *string + + // The Unix timestamp (in seconds) for when the batch was created. + CreatedAt *time.Time + + // The OpenAI API endpoint used by the batch. + Endpoint *string + + // The ID of the file containing the outputs of requests with errors. + ErrorFileID *string + + // The list of Batch errors. + Errors *BatchErrorList + + // The Unix timestamp (in seconds) for when the batch expired. + ExpiredAt *time.Time + + // The Unix timestamp (in seconds) for when the batch will expire. + ExpiresAt *time.Time + + // The Unix timestamp (in seconds) for when the batch failed. + FailedAt *time.Time + + // The Unix timestamp (in seconds) for when the batch started finalizing. + FinalizingAt *time.Time + + // The Unix timestamp (in seconds) for when the batch started processing. + InProgressAt *time.Time + + // A set of key-value pairs that can be attached to the batch. This can be useful for storing additional information about + // the batch in a structured format. + Metadata map[string]*string + + // The ID of the file containing the outputs of successfully executed requests. + OutputFileID *string + + // The request counts for different statuses within the batch. + RequestCounts *BatchRequestCounts + + // The current status of the batch. + Status *BatchStatus +} + +// BatchCreateRequest - Defines the request to create a batch. +type BatchCreateRequest struct { + // REQUIRED; The time frame within which the batch should be processed. + CompletionWindow *string + + // REQUIRED; The API endpoint used by the batch. + Endpoint *string + + // REQUIRED; The ID of the input file for the batch. + InputFileID *string + + // A set of key-value pairs that can be attached to the batch. This can be useful for storing additional information about + // the batch in a structured format. + Metadata map[string]*string +} + +// BatchCreateResponseRequestCounts - The request counts for different statuses within the batch. +type BatchCreateResponseRequestCounts struct { + // Number of requests that have been completed successfully. + Completed *int32 + + // Number of requests that have failed. + Failed *int32 + + // Total number of requests in the batch. + Total *int32 +} + +// BatchErrorDatum - A Datum containing information about a Batch Error. +type BatchErrorDatum struct { + // An error code identifying the error type. + Code *string + + // The line number of the input file where the error occurred, if applicable. + Line *int32 + + // A human-readable message providing more details about the error. + Message *string + + // The name of the parameter that caused the error, if applicable. + Param *string +} + +// BatchErrorList - A list of Batch errors. +type BatchErrorList struct { + // REQUIRED; The object type, which is always list. + Object *string + + // The list of Batch error data. + Data []BatchErrorDatum +} + +// BatchRequestCounts - The request counts for different statuses within the batch. +type BatchRequestCounts struct { + // Number of requests that have been completed successfully. + Completed *int32 + + // Number of requests that have failed. + Failed *int32 + + // Total number of requests in the batch. + Total *int32 +} + +// BatchRequestInput - The per-line object of the batch input file +type BatchRequestInput struct { + // A developer-provided per-request id that will be used to match outputs to inputs. Must be unique for each request in a + // batch. + CustomID *string + + // The HTTP method to be used for the request. Currently only POST is supported. + Method *string + + // The OpenAI API relative URL to be used for the request. Currently /v1/chat/completions, /v1/embeddings, and /v1/completions + // are supported. + URL *string +} + +// BatchRequestOutput - The per-line object of the batch output and error files +type BatchRequestOutput struct { + // A developer-provided per-request id that will be used to match outputs to inputs. + CustomID *string + + // For requests that failed with a non-HTTP error, this will contain more information on the cause of the failure. + Error *BatchRequestOutputError + + // The Id of the request. + ID *string + + // The http response + Response *BatchRequestOutputResponse +} + +// BatchRequestOutputError - For requests that failed with a non-HTTP error, this will contain more information on the cause +// of the failure. +type BatchRequestOutputError struct { + // A machine-readable error code. + Code *string + + // A human-readable error message. + Message *string +} + +// BatchRequestOutputResponse - The http response +type BatchRequestOutputResponse struct { + // The JSON body of the response + Body map[string]*string + + // An unique identifier for the OpenAI API request. Please include this request ID when contacting support. + RequestID *string + + // The HTTP status code of the response + StatusCode *int32 +} + // ChatChoice - The representation of a single prompt completion as part of an overall chat completions request. Generally, // n choices are generated per provided prompt with a default value of 1. Token limits and // other settings may limit the number of choices generated. @@ -644,10 +823,6 @@ type ChatChoice struct { // using Azure OpenAI and only when the request is configured to use enhancements. Enhancements *AzureChatEnhancements - // The reason the model stopped generating tokens, together with any applicable details. This structured representation replaces - // 'finish_reason' for some models. - FinishDetails ChatFinishDetailsClassification - // The chat message for a given chat completions prompt. Message *ChatResponseMessage } @@ -1147,7 +1322,7 @@ func (c *ChatRequestToolMessage) GetChatRequestMessage() *ChatRequestMessage { // ChatRequestUserMessage - A request chat message representing user input to the assistant. type ChatRequestUserMessage struct { // REQUIRED; The contents of the user message, with available input types varying by selected model. - Content ChatRequestUserMessageContent + Content *ChatRequestUserMessageContent // REQUIRED; The chat role associated with this message. role *ChatRole @@ -1394,7 +1569,7 @@ type ContentFilterCitedDetectionResult struct { // REQUIRED; A value indicating whether or not the content has been filtered. Filtered *bool - // REQUIRED; The license description associated with the detection. + // The license description associated with the detection. License *string // The internet location associated with the detection. @@ -1658,6 +1833,54 @@ type Error struct { message *string } +// File - Represents an assistant that can call the model and use tools. +type File struct { + // REQUIRED; The size of the file, in bytes. + Bytes *int32 + + // REQUIRED; The Unix timestamp, in seconds, representing when this object was created. + CreatedAt *time.Time + + // REQUIRED; The name of the file. + Filename *string + + // REQUIRED; The identifier, which can be referenced in API endpoints. + ID *string + + // REQUIRED; The object type, which is always 'file'. + Object *string + + // REQUIRED; The intended purpose of a file. + Purpose *FilePurpose + + // The state of the file. This field is available in Azure OpenAI only. + Status *FileState + + // The error message with details in case processing of this file failed. This field is available in Azure OpenAI only. + StatusDetails *string +} + +// FileDeletionStatus - A status response from a file deletion operation. +type FileDeletionStatus struct { + // REQUIRED; A value indicating whether deletion was successful. + Deleted *bool + + // REQUIRED; The ID of the resource specified for deletion. + ID *string + + // REQUIRED; The object type, which is always 'file'. + Object *string +} + +// FileListResponse - The response data from a file list operation. +type FileListResponse struct { + // REQUIRED; The files returned for the request. + Data []File + + // REQUIRED; The object type, which is always 'list'. + Object *string +} + // FunctionCall - The name and arguments of a function that should be called, as generated by the model. type FunctionCall struct { // REQUIRED; The arguments to call the function with, as generated by the model in JSON format. Note that the model does not @@ -1680,7 +1903,31 @@ type FunctionDefinition struct { Description *string // The parameters the function accepts, described as a JSON Schema object. - Parameters any + // REQUIRED; The function definition details for the function tool. + // NOTE: this field is JSON text that describes a JSON schema. You can marshal a data + // structure using code similar to this: + // + // jsonBytes, err := json.Marshal(map[string]any{ + // "required": []string{"location"}, + // "type": "object", + // "properties": map[string]any{ + // "location": map[string]any{ + // "type": "string", + // "description": "The city and state, e.g. San Francisco, CA", + // }, + // }, + // }) + // + // if err != nil { + // panic(err) + // } + // + // funcDef := &azopenai.FunctionDefinition{ + // Name: to.Ptr("get_current_weather"), + // Description: to.Ptr("Get the current weather in a given location"), + // Parameters: jsonBytes, + // } + Parameters []byte } // FunctionName - A structure that specifies the exact name of a specific, request-provided function to use when processing @@ -1690,6 +1937,18 @@ type FunctionName struct { Name *string } +// GetAudioTranscriptionBody - Get audio transcription body. +type GetAudioTranscriptionBody struct { + // REQUIRED; The configuration information for an audio transcription request. + Body *AudioTranscriptionOptions +} + +// GetAudioTranslationBody - Get audio translation body. +type GetAudioTranslationBody struct { + // REQUIRED; The configuration information for an audio translation request. + Body *AudioTranslationOptions +} + // ImageGenerationContentFilterResults - Describes the content filtering result for the image generation request. type ImageGenerationContentFilterResults struct { // Describes language attacks or uses that include pejorative or discriminatory language with reference to a person or identity @@ -1804,6 +2063,24 @@ type ImageGenerations struct { Data []ImageGenerationData } +// ListBatchesPage - The response data for a requested list of items. +type ListBatchesPage struct { + // REQUIRED; The object type, which is always list. + Object *string + + // The requested list of items. + Data []Batch + + // The first ID represented in this list. + FirstID *string + + // A value indicating whether there are additional values available not captured in this list. + HasMore *bool + + // The last ID represented in this list. + LastID *string +} + // MaxTokensFinishDetails - A structured representation of a stop reason that signifies a token limit was reached before the // model could naturally complete. type MaxTokensFinishDetails struct { diff --git a/sdk/ai/azopenai/models_serde.go b/sdk/ai/azopenai/models_serde.go index 1eb4390840..6440683cdb 100644 --- a/sdk/ai/azopenai/models_serde.go +++ b/sdk/ai/azopenai/models_serde.go @@ -1206,13 +1206,434 @@ func (a *AzureSearchIndexFieldMappingOptions) UnmarshalJSON(data []byte) error { return nil } +// MarshalJSON implements the json.Marshaller interface for type Batch. +func (b Batch) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populateTimeUnix(objectMap, "cancelled_at", b.CancelledAt) + populateTimeUnix(objectMap, "cancelling_at", b.CancellingAt) + populateTimeUnix(objectMap, "completed_at", b.CompletedAt) + populate(objectMap, "completion_window", b.CompletionWindow) + populateTimeUnix(objectMap, "created_at", b.CreatedAt) + populate(objectMap, "endpoint", b.Endpoint) + populate(objectMap, "error_file_id", b.ErrorFileID) + populate(objectMap, "errors", b.Errors) + populateTimeUnix(objectMap, "expired_at", b.ExpiredAt) + populateTimeUnix(objectMap, "expires_at", b.ExpiresAt) + populateTimeUnix(objectMap, "failed_at", b.FailedAt) + populateTimeUnix(objectMap, "finalizing_at", b.FinalizingAt) + populate(objectMap, "id", b.ID) + populateTimeUnix(objectMap, "in_progress_at", b.InProgressAt) + populate(objectMap, "input_file_id", b.InputFileID) + populate(objectMap, "metadata", b.Metadata) + objectMap["object"] = "batch" + populate(objectMap, "output_file_id", b.OutputFileID) + populate(objectMap, "request_counts", b.RequestCounts) + populate(objectMap, "status", b.Status) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type Batch. +func (b *Batch) 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", b, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "cancelled_at": + err = unpopulateTimeUnix(val, "CancelledAt", &b.CancelledAt) + delete(rawMsg, key) + case "cancelling_at": + err = unpopulateTimeUnix(val, "CancellingAt", &b.CancellingAt) + delete(rawMsg, key) + case "completed_at": + err = unpopulateTimeUnix(val, "CompletedAt", &b.CompletedAt) + delete(rawMsg, key) + case "completion_window": + err = unpopulate(val, "CompletionWindow", &b.CompletionWindow) + delete(rawMsg, key) + case "created_at": + err = unpopulateTimeUnix(val, "CreatedAt", &b.CreatedAt) + delete(rawMsg, key) + case "endpoint": + err = unpopulate(val, "Endpoint", &b.Endpoint) + delete(rawMsg, key) + case "error_file_id": + err = unpopulate(val, "ErrorFileID", &b.ErrorFileID) + delete(rawMsg, key) + case "errors": + err = unpopulate(val, "Errors", &b.Errors) + delete(rawMsg, key) + case "expired_at": + err = unpopulateTimeUnix(val, "ExpiredAt", &b.ExpiredAt) + delete(rawMsg, key) + case "expires_at": + err = unpopulateTimeUnix(val, "ExpiresAt", &b.ExpiresAt) + delete(rawMsg, key) + case "failed_at": + err = unpopulateTimeUnix(val, "FailedAt", &b.FailedAt) + delete(rawMsg, key) + case "finalizing_at": + err = unpopulateTimeUnix(val, "FinalizingAt", &b.FinalizingAt) + delete(rawMsg, key) + case "id": + err = unpopulate(val, "ID", &b.ID) + delete(rawMsg, key) + case "in_progress_at": + err = unpopulateTimeUnix(val, "InProgressAt", &b.InProgressAt) + delete(rawMsg, key) + case "input_file_id": + err = unpopulate(val, "InputFileID", &b.InputFileID) + delete(rawMsg, key) + case "metadata": + err = unpopulate(val, "Metadata", &b.Metadata) + delete(rawMsg, key) + case "object": + err = unpopulate(val, "Object", &b.Object) + delete(rawMsg, key) + case "output_file_id": + err = unpopulate(val, "OutputFileID", &b.OutputFileID) + delete(rawMsg, key) + case "request_counts": + err = unpopulate(val, "RequestCounts", &b.RequestCounts) + delete(rawMsg, key) + case "status": + err = unpopulate(val, "Status", &b.Status) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", b, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type BatchCreateRequest. +func (b BatchCreateRequest) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "completion_window", b.CompletionWindow) + populate(objectMap, "endpoint", b.Endpoint) + populate(objectMap, "input_file_id", b.InputFileID) + populate(objectMap, "metadata", b.Metadata) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type BatchCreateRequest. +func (b *BatchCreateRequest) 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", b, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "completion_window": + err = unpopulate(val, "CompletionWindow", &b.CompletionWindow) + delete(rawMsg, key) + case "endpoint": + err = unpopulate(val, "Endpoint", &b.Endpoint) + delete(rawMsg, key) + case "input_file_id": + err = unpopulate(val, "InputFileID", &b.InputFileID) + delete(rawMsg, key) + case "metadata": + err = unpopulate(val, "Metadata", &b.Metadata) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", b, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type BatchCreateResponseRequestCounts. +func (b BatchCreateResponseRequestCounts) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "completed", b.Completed) + populate(objectMap, "failed", b.Failed) + populate(objectMap, "total", b.Total) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type BatchCreateResponseRequestCounts. +func (b *BatchCreateResponseRequestCounts) 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", b, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "completed": + err = unpopulate(val, "Completed", &b.Completed) + delete(rawMsg, key) + case "failed": + err = unpopulate(val, "Failed", &b.Failed) + delete(rawMsg, key) + case "total": + err = unpopulate(val, "Total", &b.Total) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", b, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type BatchErrorDatum. +func (b BatchErrorDatum) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "code", b.Code) + populate(objectMap, "line", b.Line) + populate(objectMap, "message", b.Message) + populate(objectMap, "param", b.Param) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type BatchErrorDatum. +func (b *BatchErrorDatum) 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", b, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "code": + err = unpopulate(val, "Code", &b.Code) + delete(rawMsg, key) + case "line": + err = unpopulate(val, "Line", &b.Line) + delete(rawMsg, key) + case "message": + err = unpopulate(val, "Message", &b.Message) + delete(rawMsg, key) + case "param": + err = unpopulate(val, "Param", &b.Param) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", b, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type BatchErrorList. +func (b BatchErrorList) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "data", b.Data) + objectMap["object"] = "list" + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type BatchErrorList. +func (b *BatchErrorList) 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", b, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "data": + err = unpopulate(val, "Data", &b.Data) + delete(rawMsg, key) + case "object": + err = unpopulate(val, "Object", &b.Object) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", b, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type BatchRequestCounts. +func (b BatchRequestCounts) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "completed", b.Completed) + populate(objectMap, "failed", b.Failed) + populate(objectMap, "total", b.Total) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type BatchRequestCounts. +func (b *BatchRequestCounts) 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", b, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "completed": + err = unpopulate(val, "Completed", &b.Completed) + delete(rawMsg, key) + case "failed": + err = unpopulate(val, "Failed", &b.Failed) + delete(rawMsg, key) + case "total": + err = unpopulate(val, "Total", &b.Total) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", b, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type BatchRequestInput. +func (b BatchRequestInput) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "custom_id", b.CustomID) + objectMap["method"] = "POST" + populate(objectMap, "url", b.URL) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type BatchRequestInput. +func (b *BatchRequestInput) 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", b, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "custom_id": + err = unpopulate(val, "CustomID", &b.CustomID) + delete(rawMsg, key) + case "method": + err = unpopulate(val, "Method", &b.Method) + delete(rawMsg, key) + case "url": + err = unpopulate(val, "URL", &b.URL) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", b, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type BatchRequestOutput. +func (b BatchRequestOutput) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "custom_id", b.CustomID) + populate(objectMap, "error", b.Error) + populate(objectMap, "id", b.ID) + populate(objectMap, "response", b.Response) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type BatchRequestOutput. +func (b *BatchRequestOutput) 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", b, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "custom_id": + err = unpopulate(val, "CustomID", &b.CustomID) + delete(rawMsg, key) + case "error": + err = unpopulate(val, "Error", &b.Error) + delete(rawMsg, key) + case "id": + err = unpopulate(val, "ID", &b.ID) + delete(rawMsg, key) + case "response": + err = unpopulate(val, "Response", &b.Response) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", b, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type BatchRequestOutputError. +func (b BatchRequestOutputError) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "code", b.Code) + populate(objectMap, "message", b.Message) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type BatchRequestOutputError. +func (b *BatchRequestOutputError) 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", b, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "code": + err = unpopulate(val, "Code", &b.Code) + delete(rawMsg, key) + case "message": + err = unpopulate(val, "Message", &b.Message) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", b, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type BatchRequestOutputResponse. +func (b BatchRequestOutputResponse) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "body", b.Body) + populate(objectMap, "request_id", b.RequestID) + populate(objectMap, "status_code", b.StatusCode) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type BatchRequestOutputResponse. +func (b *BatchRequestOutputResponse) 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", b, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "body": + err = unpopulate(val, "Body", &b.Body) + delete(rawMsg, key) + case "request_id": + err = unpopulate(val, "RequestID", &b.RequestID) + delete(rawMsg, key) + case "status_code": + err = unpopulate(val, "StatusCode", &b.StatusCode) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", b, err) + } + } + return nil +} + // MarshalJSON implements the json.Marshaller interface for type ChatChoice. func (c ChatChoice) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) populate(objectMap, "content_filter_results", c.ContentFilterResults) populate(objectMap, "delta", c.Delta) populate(objectMap, "enhancements", c.Enhancements) - populate(objectMap, "finish_details", c.FinishDetails) populate(objectMap, "finish_reason", c.FinishReason) populate(objectMap, "index", c.Index) populate(objectMap, "logprobs", c.LogProbs) @@ -1238,9 +1659,6 @@ func (c *ChatChoice) UnmarshalJSON(data []byte) error { case "enhancements": err = unpopulate(val, "Enhancements", &c.Enhancements) delete(rawMsg, key) - case "finish_details": - c.FinishDetails, err = unmarshalChatFinishDetailsClassification(val) - delete(rawMsg, key) case "finish_reason": err = unpopulate(val, "FinishReason", &c.FinishReason) delete(rawMsg, key) @@ -2210,7 +2628,7 @@ func (c *ChatRequestToolMessage) UnmarshalJSON(data []byte) error { // MarshalJSON implements the json.Marshaller interface for type ChatRequestUserMessage. func (c ChatRequestUserMessage) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) - populateAny(objectMap, "content", c.Content) + populate(objectMap, "content", c.Content) objectMap["role"] = ChatRoleUser populate(objectMap, "name", c.Name) return json.Marshal(objectMap) @@ -3279,6 +3697,127 @@ func (e *Error) UnmarshalJSON(data []byte) error { return nil } +// MarshalJSON implements the json.Marshaller interface for type File. +func (f File) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "bytes", f.Bytes) + populateTimeUnix(objectMap, "created_at", f.CreatedAt) + populate(objectMap, "filename", f.Filename) + populate(objectMap, "id", f.ID) + objectMap["object"] = "file" + populate(objectMap, "purpose", f.Purpose) + populate(objectMap, "status", f.Status) + populate(objectMap, "status_details", f.StatusDetails) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type File. +func (f *File) 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", f, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "bytes": + err = unpopulate(val, "Bytes", &f.Bytes) + delete(rawMsg, key) + case "created_at": + err = unpopulateTimeUnix(val, "CreatedAt", &f.CreatedAt) + delete(rawMsg, key) + case "filename": + err = unpopulate(val, "Filename", &f.Filename) + delete(rawMsg, key) + case "id": + err = unpopulate(val, "ID", &f.ID) + delete(rawMsg, key) + case "object": + err = unpopulate(val, "Object", &f.Object) + delete(rawMsg, key) + case "purpose": + err = unpopulate(val, "Purpose", &f.Purpose) + delete(rawMsg, key) + case "status": + err = unpopulate(val, "Status", &f.Status) + delete(rawMsg, key) + case "status_details": + err = unpopulate(val, "StatusDetails", &f.StatusDetails) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", f, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type FileDeletionStatus. +func (f FileDeletionStatus) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "deleted", f.Deleted) + populate(objectMap, "id", f.ID) + objectMap["object"] = "file" + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type FileDeletionStatus. +func (f *FileDeletionStatus) 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", f, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "deleted": + err = unpopulate(val, "Deleted", &f.Deleted) + delete(rawMsg, key) + case "id": + err = unpopulate(val, "ID", &f.ID) + delete(rawMsg, key) + case "object": + err = unpopulate(val, "Object", &f.Object) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", f, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type FileListResponse. +func (f FileListResponse) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "data", f.Data) + objectMap["object"] = "list" + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type FileListResponse. +func (f *FileListResponse) 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", f, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "data": + err = unpopulate(val, "Data", &f.Data) + delete(rawMsg, key) + case "object": + err = unpopulate(val, "Object", &f.Object) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", f, err) + } + } + return nil +} + // MarshalJSON implements the json.Marshaller interface for type FunctionCall. func (f FunctionCall) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) @@ -3315,7 +3854,7 @@ func (f FunctionDefinition) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) populate(objectMap, "description", f.Description) populate(objectMap, "name", f.Name) - populateAny(objectMap, "parameters", f.Parameters) + populate(objectMap, "parameters", json.RawMessage(f.Parameters)) return json.Marshal(objectMap) } @@ -3335,7 +3874,9 @@ func (f *FunctionDefinition) UnmarshalJSON(data []byte) error { err = unpopulate(val, "Name", &f.Name) delete(rawMsg, key) case "parameters": - err = unpopulate(val, "Parameters", &f.Parameters) + if string(val) != "null" { + f.Parameters = val + } delete(rawMsg, key) } if err != nil { @@ -3372,6 +3913,60 @@ func (f *FunctionName) UnmarshalJSON(data []byte) error { return nil } +// MarshalJSON implements the json.Marshaller interface for type GetAudioTranscriptionBody. +func (g GetAudioTranscriptionBody) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "body", g.Body) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type GetAudioTranscriptionBody. +func (g *GetAudioTranscriptionBody) 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", g, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "body": + err = unpopulate(val, "Body", &g.Body) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", g, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type GetAudioTranslationBody. +func (g GetAudioTranslationBody) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "body", g.Body) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type GetAudioTranslationBody. +func (g *GetAudioTranslationBody) 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", g, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "body": + err = unpopulate(val, "Body", &g.Body) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", g, err) + } + } + return nil +} + // MarshalJSON implements the json.Marshaller interface for type ImageGenerationContentFilterResults. func (i ImageGenerationContentFilterResults) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) @@ -3593,6 +4188,49 @@ func (i *ImageGenerations) UnmarshalJSON(data []byte) error { return nil } +// MarshalJSON implements the json.Marshaller interface for type ListBatchesPage. +func (l ListBatchesPage) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "data", l.Data) + populate(objectMap, "first_id", l.FirstID) + populate(objectMap, "has_more", l.HasMore) + populate(objectMap, "last_id", l.LastID) + objectMap["object"] = "list" + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type ListBatchesPage. +func (l *ListBatchesPage) 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", l, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "data": + err = unpopulate(val, "Data", &l.Data) + delete(rawMsg, key) + case "first_id": + err = unpopulate(val, "FirstID", &l.FirstID) + delete(rawMsg, key) + case "has_more": + err = unpopulate(val, "HasMore", &l.HasMore) + delete(rawMsg, key) + case "last_id": + err = unpopulate(val, "LastID", &l.LastID) + delete(rawMsg, key) + case "object": + err = unpopulate(val, "Object", &l.Object) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", l, err) + } + } + return nil +} + // MarshalJSON implements the json.Marshaller interface for type MaxTokensFinishDetails. func (m MaxTokensFinishDetails) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) @@ -4341,16 +4979,6 @@ func populate(m map[string]any, k string, v any) { } } -func populateAny(m map[string]any, k string, v any) { - if v == nil { - return - } else if azcore.IsNullValue(v) { - m[k] = nil - } else { - m[k] = v - } -} - func populateByteArray[T any](m map[string]any, k string, b []T, convert func() any) { if azcore.IsNullValue(b) { m[k] = nil diff --git a/sdk/ai/azopenai/options.go b/sdk/ai/azopenai/options.go index bec502ac7f..66e92008bf 100644 --- a/sdk/ai/azopenai/options.go +++ b/sdk/ai/azopenai/options.go @@ -8,6 +8,21 @@ package azopenai +// CancelBatchOptions contains the optional parameters for the Client.CancelBatch method. +type CancelBatchOptions struct { + // placeholder for future optional parameters +} + +// CreateBatchOptions contains the optional parameters for the Client.CreateBatch method. +type CreateBatchOptions struct { + // placeholder for future optional parameters +} + +// DeleteFileOptions contains the optional parameters for the Client.DeleteFile method. +type DeleteFileOptions struct { + // placeholder for future optional parameters +} + // GenerateSpeechFromTextOptions contains the optional parameters for the Client.GenerateSpeechFromText method. type GenerateSpeechFromTextOptions struct { // placeholder for future optional parameters @@ -66,6 +81,11 @@ type getAudioTranslationInternalOptions struct { Temperature *float32 } +// GetBatchOptions contains the optional parameters for the Client.GetBatch method. +type GetBatchOptions struct { + // placeholder for future optional parameters +} + // GetChatCompletionsOptions contains the optional parameters for the Client.GetChatCompletions method. type GetChatCompletionsOptions struct { // placeholder for future optional parameters @@ -81,7 +101,38 @@ type GetEmbeddingsOptions struct { // placeholder for future optional parameters } +// GetFileContentOptions contains the optional parameters for the Client.GetFileContent method. +type GetFileContentOptions struct { + // placeholder for future optional parameters +} + +// GetFileOptions contains the optional parameters for the Client.GetFile method. +type GetFileOptions struct { + // placeholder for future optional parameters +} + // GetImageGenerationsOptions contains the optional parameters for the Client.GetImageGenerations method. type GetImageGenerationsOptions struct { // placeholder for future optional parameters } + +// ListBatchesOptions contains the optional parameters for the Client.ListBatches method. +type ListBatchesOptions struct { + // Identifier for the last event from the previous pagination request. + After *string + + // Number of batches to retrieve. Defaults to 20. + Limit *int32 +} + +// ListFilesOptions contains the optional parameters for the Client.ListFiles method. +type ListFilesOptions struct { + // A value that, when provided, limits list results to files matching the corresponding purpose. + Purpose *FilePurpose +} + +// UploadFileOptions contains the optional parameters for the Client.UploadFile method. +type UploadFileOptions struct { + // A filename to associate with the uploaded data. + Filename *string +} diff --git a/sdk/ai/azopenai/pager.go b/sdk/ai/azopenai/pager.go new file mode 100644 index 0000000000..8a343ec118 --- /dev/null +++ b/sdk/ai/azopenai/pager.go @@ -0,0 +1,64 @@ +// 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 ( + "context" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" +) + +type respType interface { + hasMore() bool + lastID() *string +} + +// newOpenAIPager is a pager that handles the OpenAI style of paging, where you pass a "lastID" +// to indicate where in the chain of items you are. +// +// NOTE: the OptionsT/POptionsT is to handle the odd ambiguity in generics where you have a +// pointer-to-something and something can't be converted. +func newOpenAIPager[ResponseT respType, OptionsT any, POptionsT interface { + *OptionsT + updateAfter(after *string) +}]( + client *Client, + nextPageFn func(ctx context.Context, opts POptionsT) (ResponseT, error), + options POptionsT) *runtime.Pager[ResponseT] { + var lastID *string + + first := true + + return runtime.NewPager(runtime.PagingHandler[ResponseT]{ + More: func(clmr ResponseT) bool { + return clmr.hasMore() + }, + Fetcher: func(ctx context.Context, clmr *ResponseT) (ResponseT, error) { + newOptions := options + + if newOptions == nil { + var zero OptionsT + newOptions = &zero + } + + if !first { + // make sure to respect the callers choice on the first time through. + newOptions.updateAfter(lastID) + } + + first = false + + resp, err := nextPageFn(ctx, newOptions) + + if err != nil { + var zero ResponseT + return zero, err + } + + lastID = resp.lastID() + return resp, nil + }, + Tracer: client.internal.Tracer(), + }) +} diff --git a/sdk/ai/azopenai/polymorphic_helpers.go b/sdk/ai/azopenai/polymorphic_helpers.go index 8a464d71ec..8b37290b4b 100644 --- a/sdk/ai/azopenai/polymorphic_helpers.go +++ b/sdk/ai/azopenai/polymorphic_helpers.go @@ -166,29 +166,6 @@ func unmarshalChatCompletionsToolDefinitionClassificationArray(rawMsg json.RawMe return fArray, nil } -func unmarshalChatFinishDetailsClassification(rawMsg json.RawMessage) (ChatFinishDetailsClassification, error) { - if rawMsg == nil || string(rawMsg) == "null" { - return nil, nil - } - var m map[string]any - if err := json.Unmarshal(rawMsg, &m); err != nil { - return nil, err - } - var b ChatFinishDetailsClassification - switch m["type"] { - case "max_tokens": - b = &MaxTokensFinishDetails{} - case "stop": - b = &StopFinishDetails{} - default: - b = &ChatFinishDetails{} - } - if err := json.Unmarshal(rawMsg, b); err != nil { - return nil, err - } - return b, nil -} - func unmarshalChatRequestMessageClassification(rawMsg json.RawMessage) (ChatRequestMessageClassification, error) { if rawMsg == nil || string(rawMsg) == "null" { return nil, nil diff --git a/sdk/ai/azopenai/responses.go b/sdk/ai/azopenai/responses.go index 1715b29d2e..5c8b0a94e7 100644 --- a/sdk/ai/azopenai/responses.go +++ b/sdk/ai/azopenai/responses.go @@ -10,6 +10,24 @@ package azopenai import "io" +// CancelBatchResponse contains the response from method Client.CancelBatch. +type CancelBatchResponse struct { + // The Batch object. + Batch +} + +// CreateBatchResponse contains the response from method Client.CreateBatch. +type CreateBatchResponse struct { + // The Batch object. + Batch +} + +// DeleteFileResponse contains the response from method Client.DeleteFile. +type DeleteFileResponse struct { + // A status response from a file deletion operation. + FileDeletionStatus +} + // GenerateSpeechFromTextResponse contains the response from method Client.GenerateSpeechFromText. type GenerateSpeechFromTextResponse struct { // Body contains the streaming response. @@ -28,6 +46,12 @@ type getAudioTranslationInternalResponse struct { AudioTranslation } +// GetBatchResponse contains the response from method Client.GetBatch. +type GetBatchResponse struct { + // The Batch object. + Batch +} + // GetChatCompletionsResponse contains the response from method Client.GetChatCompletions. type GetChatCompletionsResponse struct { // Representation of the response data from a chat completions request. @@ -52,8 +76,37 @@ type GetEmbeddingsResponse struct { Embeddings } +// GetFileContentResponse contains the response from method Client.GetFileContent. +type GetFileContentResponse struct { + Value []byte +} + +// GetFileResponse contains the response from method Client.GetFile. +type GetFileResponse struct { + // Represents an assistant that can call the model and use tools. + File +} + // GetImageGenerationsResponse contains the response from method Client.GetImageGenerations. type GetImageGenerationsResponse struct { // The result of a successful image generation operation. ImageGenerations } + +// ListBatchesResponse contains the response from method Client.ListBatches. +type ListBatchesResponse struct { + // The response data for a requested list of items. + ListBatchesPage +} + +// ListFilesResponse contains the response from method Client.ListFiles. +type ListFilesResponse struct { + // The response data from a file list operation. + FileListResponse +} + +// UploadFileResponse contains the response from method Client.UploadFile. +type UploadFileResponse struct { + // Represents an assistant that can call the model and use tools. + File +} diff --git a/sdk/ai/azopenai/testdata/package-lock.json b/sdk/ai/azopenai/testdata/package-lock.json index a4f8a0134b..a8bce4edfc 100644 --- a/sdk/ai/azopenai/testdata/package-lock.json +++ b/sdk/ai/azopenai/testdata/package-lock.json @@ -8,46 +8,52 @@ "name": "testdata", "version": "0.1.0", "dependencies": { - "@azure-tools/typespec-autorest": "^0.42.0", - "@azure-tools/typespec-azure-core": "^0.42.0", - "@typespec/compiler": "^0.56.0", - "@typespec/openapi3": "^0.56.0" + "@azure-tools/typespec-autorest": "^0.44.1", + "@azure-tools/typespec-azure-core": "~0.44.0", + "@typespec/compiler": "^0.58.1", + "@typespec/openapi3": "~0.58.0" } }, + "node_modules/@apidevtools/swagger-methods": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==" + }, "node_modules/@azure-tools/typespec-autorest": { - "version": "0.42.1", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-autorest/-/typespec-autorest-0.42.1.tgz", - "integrity": "sha512-egWR2Ljxde5PvyDwuVu/8Rq8cSW6FW+qMbvwHfzrtm/sULdSnA6k+VnGD/PQ+dk+1SmFeKG1b+0MlaFuBVz1Hw==", + "version": "0.44.1", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-autorest/-/typespec-autorest-0.44.1.tgz", + "integrity": "sha512-lw/iM659GuFgckDeRFFu0vx6wGBy814n+mjzbpi0Qwjvj8/hYULSjpty9P4WBDE30rYCUde1pWX5nK6TnwhOkQ==", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "~0.42.0", - "@azure-tools/typespec-client-generator-core": "~0.42.0", - "@typespec/compiler": "~0.56.0", - "@typespec/http": "~0.56.0", - "@typespec/openapi": "~0.56.0", - "@typespec/rest": "~0.56.0", - "@typespec/versioning": "~0.56.0" + "@azure-tools/typespec-azure-core": "~0.44.0", + "@azure-tools/typespec-azure-resource-manager": "~0.44.0", + "@azure-tools/typespec-client-generator-core": "~0.44.2", + "@typespec/compiler": "~0.58.0", + "@typespec/http": "~0.58.0", + "@typespec/openapi": "~0.58.0", + "@typespec/rest": "~0.58.0", + "@typespec/versioning": "~0.58.0" } }, "node_modules/@azure-tools/typespec-azure-core": { - "version": "0.42.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-core/-/typespec-azure-core-0.42.0.tgz", - "integrity": "sha512-8C96RkgSWtgqsaHRMWCd2iDltFJZTGmFQiTZazZj/uRy0Wn1ikjSriSN8t1puL5SiUPd0BVJP/YXiwAfjfZYDA==", + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-core/-/typespec-azure-core-0.44.0.tgz", + "integrity": "sha512-d11QK2v5fOZH8YUqf42FsqHEirKCHzeKFq4Uo/51BXCXmJJahsTaFMAG2M0GoJe8tmTHeMijStnVMfzcGNqCAA==", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "@typespec/compiler": "~0.56.0", - "@typespec/http": "~0.56.0", - "@typespec/rest": "~0.56.0" + "@typespec/compiler": "~0.58.0", + "@typespec/http": "~0.58.0", + "@typespec/rest": "~0.58.0" } }, - "node_modules/@azure-tools/typespec-client-generator-core": { - "version": "0.42.3", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-client-generator-core/-/typespec-client-generator-core-0.42.3.tgz", - "integrity": "sha512-ZDVVIY1uJ8EaI4QhCdQmTdKVACm4xYn/I7ySpwv4oxk9X8kZFhxx+PKNHAlx34mhOf4oF0PW3wCN5DMeU6asYg==", + "node_modules/@azure-tools/typespec-azure-resource-manager": { + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-resource-manager/-/typespec-azure-resource-manager-0.44.0.tgz", + "integrity": "sha512-m4dG41at6En1swbxlvCDl1v4Mvrfp17acDnRxEcd4SdKP2R9eVS2mBy1tSuFtMcJlOnoBZ5CxQgk+Osg/Q9nmA==", "peer": true, "dependencies": { "change-case": "~5.4.4", @@ -57,19 +63,41 @@ "node": ">=18.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "~0.42.0", - "@typespec/compiler": "~0.56.0", - "@typespec/http": "~0.56.0", - "@typespec/rest": "~0.56.0", - "@typespec/versioning": "~0.56.0" + "@azure-tools/typespec-azure-core": "~0.44.0", + "@typespec/compiler": "~0.58.0", + "@typespec/http": "~0.58.0", + "@typespec/openapi": "~0.58.0", + "@typespec/rest": "~0.58.0", + "@typespec/versioning": "~0.58.0" + } + }, + "node_modules/@azure-tools/typespec-client-generator-core": { + "version": "0.44.3", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-client-generator-core/-/typespec-client-generator-core-0.44.3.tgz", + "integrity": "sha512-HXjxQs7ELrTuIDqOjlYhP4rM4AXb143klbiM8dkEGtqNBRCk77gVCGYVH1M3kWKAEs0dQKhzoUukscqRsfELuw==", + "peer": true, + "dependencies": { + "change-case": "~5.4.4", + "pluralize": "^8.0.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@azure-tools/typespec-azure-core": "~0.44.0", + "@typespec/compiler": "~0.58.0", + "@typespec/http": "~0.58.0", + "@typespec/openapi": "~0.58.0", + "@typespec/rest": "~0.58.0", + "@typespec/versioning": "~0.58.0" } }, "node_modules/@babel/code-frame": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", - "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", "dependencies": { - "@babel/highlight": "^7.24.2", + "@babel/highlight": "^7.24.7", "picocolors": "^1.0.0" }, "engines": { @@ -77,19 +105,19 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", - "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", - "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", "dependencies": { - "@babel/helper-validator-identifier": "^7.24.5", + "@babel/helper-validator-identifier": "^7.24.7", "chalk": "^2.4.2", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" @@ -98,6 +126,30 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/runtime": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", + "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@humanwhocodes/momoa": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@humanwhocodes/momoa/-/momoa-2.0.4.tgz", + "integrity": "sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==", + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -130,6 +182,130 @@ "node": ">= 8" } }, + "node_modules/@readme/better-ajv-errors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@readme/better-ajv-errors/-/better-ajv-errors-1.6.0.tgz", + "integrity": "sha512-9gO9rld84Jgu13kcbKRU+WHseNhaVt76wYMeRDGsUGYxwJtI3RmEJ9LY9dZCYQGI8eUZLuxb5qDja0nqklpFjQ==", + "dependencies": { + "@babel/code-frame": "^7.16.0", + "@babel/runtime": "^7.21.0", + "@humanwhocodes/momoa": "^2.0.3", + "chalk": "^4.1.2", + "json-to-ast": "^2.0.3", + "jsonpointer": "^5.0.0", + "leven": "^3.1.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "ajv": "4.11.8 - 8" + } + }, + "node_modules/@readme/better-ajv-errors/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@readme/better-ajv-errors/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@readme/better-ajv-errors/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@readme/better-ajv-errors/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@readme/better-ajv-errors/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@readme/better-ajv-errors/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@readme/json-schema-ref-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@readme/json-schema-ref-parser/-/json-schema-ref-parser-1.2.0.tgz", + "integrity": "sha512-Bt3QVovFSua4QmHa65EHUmh2xS0XJ3rgTEUPH998f4OW4VVJke3BuS16f+kM0ZLOGdvIrzrPRqwihuv5BAjtrA==", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.6", + "call-me-maybe": "^1.0.1", + "js-yaml": "^4.1.0" + } + }, + "node_modules/@readme/openapi-parser": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@readme/openapi-parser/-/openapi-parser-2.6.0.tgz", + "integrity": "sha512-pyFJXezWj9WI1O+gdp95CoxfY+i+Uq3kKk4zXIFuRAZi9YnHpHOpjumWWr67wkmRTw19Hskh9spyY0Iyikf3fA==", + "dependencies": { + "@apidevtools/swagger-methods": "^3.0.2", + "@jsdevtools/ono": "^7.1.3", + "@readme/better-ajv-errors": "^1.6.0", + "@readme/json-schema-ref-parser": "^1.2.0", + "@readme/openapi-schemas": "^3.1.0", + "ajv": "^8.12.0", + "ajv-draft-04": "^1.0.0", + "call-me-maybe": "^1.0.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "openapi-types": ">=7" + } + }, + "node_modules/@readme/openapi-schemas": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@readme/openapi-schemas/-/openapi-schemas-3.1.0.tgz", + "integrity": "sha512-9FC/6ho8uFa8fV50+FPy/ngWN53jaUu4GRXlAjcxIRrzhltJnpKkBG2Tp0IDraFJeWrOpk84RJ9EMEEYzaI1Bw==", + "engines": { + "node": ">=18" + } + }, "node_modules/@sindresorhus/merge-streams": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", @@ -141,23 +317,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + }, "node_modules/@typespec/compiler": { - "version": "0.56.0", - "resolved": "https://registry.npmjs.org/@typespec/compiler/-/compiler-0.56.0.tgz", - "integrity": "sha512-K+VhXycoeqcoSGtB0/l1XYco4V2qRsCOOwqklVM4Yew7kTcKVfz7CT7a6a2OKWDMNg5iijZtRBoM5YF50XtQug==", + "version": "0.58.1", + "resolved": "https://registry.npmjs.org/@typespec/compiler/-/compiler-0.58.1.tgz", + "integrity": "sha512-bVxxM35r40OtuL4+/9W/g1EevlnWnW6i151nsZAFOJj1xWHoE2G9zkx5/Feic8OlzArjhGGLJOLH3Ez1Wrw35A==", "dependencies": { - "@babel/code-frame": "~7.24.2", - "ajv": "~8.12.0", + "@babel/code-frame": "~7.24.7", + "ajv": "~8.16.0", "change-case": "~5.4.4", - "globby": "~14.0.1", + "globby": "~14.0.2", "mustache": "~4.2.0", - "picocolors": "~1.0.0", - "prettier": "~3.2.5", + "picocolors": "~1.0.1", + "prettier": "~3.3.2", "prompts": "~2.4.2", - "semver": "^7.6.0", + "semver": "^7.6.2", + "temporal-polyfill": "^0.2.5", "vscode-languageserver": "~9.0.1", "vscode-languageserver-textdocument": "~1.0.11", - "yaml": "~2.4.1", + "yaml": "~2.4.5", "yargs": "~17.7.2" }, "bin": { @@ -169,87 +351,104 @@ } }, "node_modules/@typespec/http": { - "version": "0.56.0", - "resolved": "https://registry.npmjs.org/@typespec/http/-/http-0.56.0.tgz", - "integrity": "sha512-f/tpHRWev9bnAtNPFkfCU/5SFou9glA/rPDY0m2W5bK6EG1/6/TKKKz5FoKPA4xvc2dQ5vu/ouGLb4i5UzXvWQ==", + "version": "0.58.0", + "resolved": "https://registry.npmjs.org/@typespec/http/-/http-0.58.0.tgz", + "integrity": "sha512-jQpkugg9AZVrNDMkDIgZRpIoRkkU2b0LtKWqMGg33MItYj9/DYSgDtY7xb7oCBppRtFFZ/h138HyhYl3zQxZRg==", "peer": true, "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "@typespec/compiler": "~0.56.0" + "@typespec/compiler": "~0.58.0" } }, "node_modules/@typespec/openapi": { - "version": "0.56.0", - "resolved": "https://registry.npmjs.org/@typespec/openapi/-/openapi-0.56.0.tgz", - "integrity": "sha512-q8+IHRglXBm3slvonRLSNYN2fX7plbWA+ugIiMJZTeyc3enqfxPqMGA8BCiAFV3kwP0uPPpIXbCSIVhHgkONbA==", + "version": "0.58.0", + "resolved": "https://registry.npmjs.org/@typespec/openapi/-/openapi-0.58.0.tgz", + "integrity": "sha512-gu6nXfmpfZrfq8Etpgl1dpMfsXii7EzQyhZgsPhIy7ZwV5bDmFk1/oyhTqIpWrnr4pD3r151T2BQjzJefjf15A==", "peer": true, "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "@typespec/compiler": "~0.56.0", - "@typespec/http": "~0.56.0" + "@typespec/compiler": "~0.58.0", + "@typespec/http": "~0.58.0" } }, "node_modules/@typespec/openapi3": { - "version": "0.56.0", - "resolved": "https://registry.npmjs.org/@typespec/openapi3/-/openapi3-0.56.0.tgz", - "integrity": "sha512-55JPUP7dFk4iXn4fNKZEs76j7hAdlWfoMWNPsQPRJCP//KWCtNXfTP+/TTVPVv1L/6HztbXyPV0agKZwyS7gDw==", + "version": "0.58.0", + "resolved": "https://registry.npmjs.org/@typespec/openapi3/-/openapi3-0.58.0.tgz", + "integrity": "sha512-G9t9CWT9cN6ip39dLZaE6JdEDxGsFyOUxA2s6a087rweoTH85XzsFiQL7uiUD8vHhXyEo6tF6sy3LMZVN0BsoQ==", "dependencies": { - "yaml": "~2.4.1" + "@readme/openapi-parser": "~2.6.0", + "yaml": "~2.4.5" + }, + "bin": { + "tsp-openapi3": "cmd/tsp-openapi3.js" }, "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "@typespec/compiler": "~0.56.0", - "@typespec/http": "~0.56.0", - "@typespec/openapi": "~0.56.0", - "@typespec/versioning": "~0.56.0" + "@typespec/compiler": "~0.58.0", + "@typespec/http": "~0.58.0", + "@typespec/openapi": "~0.58.0", + "@typespec/versioning": "~0.58.0" } }, "node_modules/@typespec/rest": { - "version": "0.56.0", - "resolved": "https://registry.npmjs.org/@typespec/rest/-/rest-0.56.0.tgz", - "integrity": "sha512-8w4WhWDcpEQNW8bB1BHhiBxIQUChDJtyq/n9p2OI/Bm1wncd61y/ZNOtcxmlKq8uB9d+dzHiZdEfqFCR8HF8/Q==", + "version": "0.58.0", + "resolved": "https://registry.npmjs.org/@typespec/rest/-/rest-0.58.0.tgz", + "integrity": "sha512-QBxkED0/KQKG22pwzis0n7BY+uLMSZZPSoVe/ESBFika9n5/yyeQ0l58xbFFwwfxAxe4xwuZ5PNwTdEXZbzr5g==", "peer": true, "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "@typespec/compiler": "~0.56.0", - "@typespec/http": "~0.56.0" + "@typespec/compiler": "~0.58.0", + "@typespec/http": "~0.58.0" } }, "node_modules/@typespec/versioning": { - "version": "0.56.0", - "resolved": "https://registry.npmjs.org/@typespec/versioning/-/versioning-0.56.0.tgz", - "integrity": "sha512-j7IN9XFyGn3LH6IOJkinEvk9sDncsxiWPULOAe0VQ+D/dtCfLawDMUALnvklMDRKeD1OOUPSCjjUAp9OB0f7YA==", + "version": "0.58.0", + "resolved": "https://registry.npmjs.org/@typespec/versioning/-/versioning-0.58.0.tgz", + "integrity": "sha512-brnQQ3wKWh4AbgqmnVLj+8zyOaDk9VPWg4QBecdQxzz7PrSrlAzIzRfeIyr67+hwi/0SvkTAB6GNH7YYTypKGA==", "peer": true, "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "@typespec/compiler": "~0.56.0" + "@typespec/compiler": "~0.58.0" } }, "node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz", + "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==", "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js": "^4.4.1" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -269,6 +468,11 @@ "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.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -280,6 +484,11 @@ "node": ">=8" } }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==" + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -311,6 +520,14 @@ "node": ">=12" } }, + "node_modules/code-error-fragment": { + "version": "0.0.230", + "resolved": "https://registry.npmjs.org/code-error-fragment/-/code-error-fragment-0.0.230.tgz", + "integrity": "sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw==", + "engines": { + "node": ">= 4" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -404,9 +621,9 @@ } }, "node_modules/globby": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.1.tgz", - "integrity": "sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==", + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz", + "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==", "dependencies": { "@sindresorhus/merge-streams": "^2.1.0", "fast-glob": "^3.3.2", @@ -422,6 +639,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" + }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -478,11 +700,42 @@ "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", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, + "node_modules/json-to-ast": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/json-to-ast/-/json-to-ast-2.1.0.tgz", + "integrity": "sha512-W9Lq347r8tA1DfMvAGn9QNcgYm4Wm7Yc+k8e6vezpMnRT+NHbtlxgNBXRVjXe9YM6eTn6+p/MKOlV/aABJcSnQ==", + "dependencies": { + "code-error-fragment": "0.0.230", + "grapheme-splitter": "^1.0.4" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -491,6 +744,14 @@ "node": ">=6" } }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "engines": { + "node": ">=6" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -500,12 +761,12 @@ } }, "node_modules/micromatch": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.6.tgz", - "integrity": "sha512-Y4Ypn3oujJYxJcMacVgcs92wofTHxp9FzfDpQON4msDefoC0lb3ETvQLOdLcbhSwU1bz8HrL/1sygfBIHudrkQ==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", "dependencies": { "braces": "^3.0.3", - "picomatch": "^4.0.2" + "picomatch": "^2.3.1" }, "engines": { "node": ">=8.6" @@ -519,6 +780,12 @@ "mustache": "bin/mustache" } }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", + "peer": true + }, "node_modules/path-type": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", @@ -536,11 +803,11 @@ "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" }, "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "engines": { - "node": ">=12" + "node": ">=8.6" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -556,9 +823,9 @@ } }, "node_modules/prettier": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", - "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "bin": { "prettier": "bin/prettier.cjs" }, @@ -608,6 +875,11 @@ } ] }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -717,6 +989,19 @@ "node": ">=4" } }, + "node_modules/temporal-polyfill": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/temporal-polyfill/-/temporal-polyfill-0.2.5.tgz", + "integrity": "sha512-ye47xp8Cb0nDguAhrrDS1JT1SzwEV9e26sSsrWzVu+yPZ7LzceEcH0i2gci9jWfOfSCCgM3Qv5nOYShVUUFUXA==", + "dependencies": { + "temporal-spec": "^0.2.4" + } + }, + "node_modules/temporal-spec": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/temporal-spec/-/temporal-spec-0.2.4.tgz", + "integrity": "sha512-lDMFv4nKQrSjlkHKAlHVqKrBG4DyFfa9F74cmBZ3Iy3ed8yvWnlWSIdi4IKfSqwmazAohBNwiN64qGx4y5Q3IQ==" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -840,9 +1125,9 @@ } }, "node_modules/yaml": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz", - "integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==", + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz", + "integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==", "bin": { "yaml": "bin.mjs" }, diff --git a/sdk/ai/azopenai/testdata/package.json b/sdk/ai/azopenai/testdata/package.json index c9d009795f..ee6b5d74d0 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.42.0", - "@azure-tools/typespec-azure-core": "^0.42.0", - "@typespec/compiler": "^0.56.0", - "@typespec/openapi3": "^0.56.0" + "@azure-tools/typespec-autorest": "^0.44.1", + "@azure-tools/typespec-azure-core": "~0.44.0", + "@typespec/compiler": "^0.58.1", + "@typespec/openapi3": "~0.58.0" }, "private": true } diff --git a/sdk/ai/azopenai/testdata/tsp-location.yaml b/sdk/ai/azopenai/testdata/tsp-location.yaml index d4d652513f..89c2f35eee 100644 --- a/sdk/ai/azopenai/testdata/tsp-location.yaml +++ b/sdk/ai/azopenai/testdata/tsp-location.yaml @@ -1,3 +1,3 @@ directory: specification/cognitiveservices/OpenAI.Inference -commit: f6fbf63641332dd06c025d995a82dc090a5f6fad +commit: e8a00d5eb5252d05521a7ef34edcc7d99fff6b3c repo: Azure/azure-rest-api-specs