diff --git a/sdk/ai/azopenai/CHANGELOG.md b/sdk/ai/azopenai/CHANGELOG.md index 0efe2ae8ab..26a1ffc945 100644 --- a/sdk/ai/azopenai/CHANGELOG.md +++ b/sdk/ai/azopenai/CHANGELOG.md @@ -1,9 +1,12 @@ # Release History -## 0.1.2 (Unreleased) +## 0.2.0 (2023-08-28) ### Features Added +- ChatCompletions supports Azure OpenAI's newest feature to use Azure OpenAI with your own data. See `example_client_getchatcompletions_extensions_test.go` + for a working example. (PR#21426) + ### Breaking Changes - ChatCompletionsOptions, CompletionsOptions, EmbeddingsOptions `DeploymentID` field renamed to `Deployment`. @@ -13,9 +16,7 @@ - EventReader, used by GetChatCompletionsStream and GetCompletionsStream for streaming results, would not return an error if the underlying Body reader was closed or EOF'd before the actual DONE: token arrived. This could result in an - infinite loop for callers. (PR#) - -### Other Changes + infinite loop for callers. (PR#21323) ## 0.1.1 (2023-07-26) diff --git a/sdk/ai/azopenai/assets.json b/sdk/ai/azopenai/assets.json index 88dadfd79f..1d2632ea7d 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_2bf13bba09" + "Tag": "go/ai/azopenai_7be6ae3c15" } diff --git a/sdk/ai/azopenai/autorest.md b/sdk/ai/azopenai/autorest.md index cbdb62a780..664d9f0ed6 100644 --- a/sdk/ai/azopenai/autorest.md +++ b/sdk/ai/azopenai/autorest.md @@ -100,6 +100,15 @@ directive: - from: openapi-document where: $.components.schemas["ChatChoice"].properties.finish_reason transform: $["$ref"] = "#/components/schemas/CompletionsFinishReason"; delete $.oneOf; + - from: openapi-document + where: $.components.schemas["AzureChatExtensionConfiguration"].properties.type + transform: $["$ref"] = "#/components/schemas/AzureChatExtensionType"; delete $.allOf; + - from: openapi-document + where: $.components.schemas["AzureChatExtensionConfiguration"].properties.type + transform: $["$ref"] = "#/components/schemas/AzureChatExtensionType"; delete $.allOf; + - from: openapi-document + where: $.components.schemas["AzureCognitiveSearchChatExtensionConfiguration"].properties.queryType + transform: $["$ref"] = "#/components/schemas/AzureCognitiveSearchQueryType"; delete $.allOf; # Fix "AutoGenerated" models - from: openapi-document where: $.components.schemas["ChatCompletions"].properties.usage @@ -294,4 +303,45 @@ directive: return $ .replace(/populate\(objectMap, "model", (c|e).Model\)/g, 'populate(objectMap, "model", &$1.Deployment)') .replace(/err = unpopulate\(val, "Model", &(c|e).Model\)/g, 'err = unpopulate(val, "Model", &$1.Deployment)'); + + # Make the Azure extensions internal - we expose these through the GetChatCompletions*() functions + # and just treat which endpoint we use as an implementation detail. + - from: client.go + where: $ + transform: | + return $ + .replace(/GetChatCompletionsWithAzureExtensions([ (])/g, "getChatCompletionsWithAzureExtensions$1") + .replace(/GetChatCompletions([ (])/g, "getChatCompletions$1"); + + # move the Azure extensions options into place + - from: models.go + where: $ + transform: return $.replace(/(\/\/ The configuration entries for Azure OpenAI.+?)DataSources \[\]AzureChatExtensionConfiguration/s, "$1AzureExtensionsOptions *AzureChatExtensionOptions"); + - from: models_serde.go + where: $ + transform: | + return $ + .replace(/populate\(objectMap, "dataSources", c.DataSources\)/, 'if c.AzureExtensionsOptions != nil { populate(objectMap, "dataSources", c.AzureExtensionsOptions.Extensions) }') + // technically not used, but let's be completionists... + .replace(/err = unpopulate\(val, "DataSources", &c.DataSources\)/, 'c.AzureExtensionsOptions = &AzureChatExtensionOptions{}; err = unpopulate(val, "DataSources", &c.AzureExtensionsOptions.Extensions)') + + # try to fix some of the generated types. + + # swap the `Parameters` and `Type` fields (Type really drives what's in Parameters) + - from: models.go + where: $ + transform: | + let typeRE = /(\/\/ REQUIRED; The label for the type of an Azure chat extension.*?Type \*AzureChatExtensionType)/s; + let paramsRE = /(\/\/ REQUIRED; The configuration payload used for the Azure chat extension.*?Parameters any)/s; + + return $ + .replace(paramsRE, "") + .replace(typeRE, $.match(typeRE)[1] + "\n\n" + $.match(paramsRE)[1]); + + - from: constants.go + where: $ + transform: | + return $.replace( + /(AzureChatExtensionTypeAzureCognitiveSearch AzureChatExtensionType)/, + "// AzureChatExtensionTypeAzureCognitiveSearch enables the use of an Azure Cognitive Search index with chat completions.\n// [AzureChatExtensionConfiguration.Parameter] should be of type [AzureCognitiveSearchChatExtensionConfiguration].\n$1"); ``` diff --git a/sdk/ai/azopenai/ci.yml b/sdk/ai/azopenai/ci.yml index 2bfcd1c974..39e2390cb8 100644 --- a/sdk/ai/azopenai/ci.yml +++ b/sdk/ai/azopenai/ci.yml @@ -55,3 +55,9 @@ stages: OPENAI_EMBEDDINGS_MODEL: $(OPENAI-EMBEDDINGS-MODEL) OPENAI_CHAT_COMPLETIONS_MODEL: $(OPENAI-CHAT-COMPLETIONS-MODEL) OPENAI_COMPLETIONS_MODEL: $(OPENAI-COMPLETIONS-MODEL) + + # used for BYOD scenarios with ChatCompletions + COGNITIVE_SEARCH_API_ENDPOINT: $(COGNITIVE-SEARCH-API-ENDPOINT) + COGNITIVE_SEARCH_API_INDEX: $(COGNITIVE-SEARCH-API-INDEX) + COGNITIVE_SEARCH_API_KEY: $(COGNITIVE-SEARCH-API-KEY) + diff --git a/sdk/ai/azopenai/client.go b/sdk/ai/azopenai/client.go index cfdc2c6df7..9ffe12f5e2 100644 --- a/sdk/ai/azopenai/client.go +++ b/sdk/ai/azopenai/client.go @@ -27,7 +27,7 @@ type Client struct { // beginAzureBatchImageGeneration - Starts the generation of a batch of images from a text caption // If the operation fails it returns an *azcore.ResponseError type. // -// Generated from API version 2023-07-01-preview +// Generated from API version 2023-08-01-preview // - options - beginAzureBatchImageGenerationOptions contains the optional parameters for the Client.beginAzureBatchImageGeneration // method. func (client *Client) beginAzureBatchImageGeneration(ctx context.Context, body ImageGenerationOptions, options *beginAzureBatchImageGenerationOptions) (*runtime.Poller[azureBatchImageGenerationInternalResponse], error) { @@ -46,7 +46,7 @@ func (client *Client) beginAzureBatchImageGeneration(ctx context.Context, body I // AzureBatchImageGenerationInternal - Starts the generation of a batch of images from a text caption // If the operation fails it returns an *azcore.ResponseError type. // -// Generated from API version 2023-07-01-preview +// Generated from API version 2023-08-01-preview func (client *Client) azureBatchImageGenerationInternal(ctx context.Context, body ImageGenerationOptions, options *beginAzureBatchImageGenerationOptions) (*http.Response, error) { var err error req, err := client.azureBatchImageGenerationInternalCreateRequest(ctx, body, options) @@ -72,7 +72,7 @@ func (client *Client) azureBatchImageGenerationInternalCreateRequest(ctx context return nil, err } reqQP := req.Raw().URL.Query() - reqQP.Set("api-version", "2023-07-01-preview") + reqQP.Set("api-version", "2023-08-01-preview") req.Raw().URL.RawQuery = reqQP.Encode() req.Raw().Header["Accept"] = []string{"application/json"} if err := runtime.MarshalAsJSON(req, body); err != nil { @@ -81,13 +81,13 @@ func (client *Client) azureBatchImageGenerationInternalCreateRequest(ctx context return req, nil } -// GetChatCompletions - Gets chat completions for the provided chat messages. Completions support a wide variety of tasks +// getChatCompletions - Gets chat completions for the provided chat messages. Completions support a wide variety of tasks // and generate text that continues from or "completes" provided prompt data. // If the operation fails it returns an *azcore.ResponseError type. // -// Generated from API version 2023-07-01-preview -// - options - GetChatCompletionsOptions contains the optional parameters for the Client.GetChatCompletions method. -func (client *Client) GetChatCompletions(ctx context.Context, body ChatCompletionsOptions, options *GetChatCompletionsOptions) (GetChatCompletionsResponse, error) { +// Generated from API version 2023-08-01-preview +// - options - GetChatCompletionsOptions contains the optional parameters for the Client.getChatCompletions method. +func (client *Client) getChatCompletions(ctx context.Context, body ChatCompletionsOptions, options *GetChatCompletionsOptions) (GetChatCompletionsResponse, error) { var err error req, err := client.getChatCompletionsCreateRequest(ctx, body, options) if err != nil { @@ -105,7 +105,7 @@ func (client *Client) GetChatCompletions(ctx context.Context, body ChatCompletio return resp, err } -// getChatCompletionsCreateRequest creates the GetChatCompletions request. +// getChatCompletionsCreateRequest creates the getChatCompletions request. func (client *Client) getChatCompletionsCreateRequest(ctx context.Context, body ChatCompletionsOptions, options *GetChatCompletionsOptions) (*policy.Request, error) { urlPath := "chat/completions" req, err := runtime.NewRequest(ctx, http.MethodPost, client.formatURL(urlPath, getDeployment(body))) @@ -113,7 +113,7 @@ func (client *Client) getChatCompletionsCreateRequest(ctx context.Context, body return nil, err } reqQP := req.Raw().URL.Query() - reqQP.Set("api-version", "2023-07-01-preview") + reqQP.Set("api-version", "2023-08-01-preview") req.Raw().URL.RawQuery = reqQP.Encode() req.Raw().Header["Accept"] = []string{"application/json"} if err := runtime.MarshalAsJSON(req, body); err != nil { @@ -122,7 +122,7 @@ func (client *Client) getChatCompletionsCreateRequest(ctx context.Context, body return req, nil } -// getChatCompletionsHandleResponse handles the GetChatCompletions response. +// getChatCompletionsHandleResponse handles the getChatCompletions response. func (client *Client) getChatCompletionsHandleResponse(resp *http.Response) (GetChatCompletionsResponse, error) { result := GetChatCompletionsResponse{} if err := runtime.UnmarshalAsJSON(resp, &result.ChatCompletions); err != nil { @@ -131,11 +131,63 @@ func (client *Client) getChatCompletionsHandleResponse(resp *http.Response) (Get return result, nil } +// getChatCompletionsWithAzureExtensions - Gets chat completions for the provided chat messages. This is an Azure-specific +// version of chat completions that supports integration with configured data sources and other augmentations to the base +// chat completions capabilities. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2023-08-01-preview +// - options - GetChatCompletionsWithAzureExtensionsOptions contains the optional parameters for the Client.GetChatCompletionsWithAzureExtensions +// method. +func (client *Client) getChatCompletionsWithAzureExtensions(ctx context.Context, body ChatCompletionsOptions, options *GetChatCompletionsWithAzureExtensionsOptions) (GetChatCompletionsWithAzureExtensionsResponse, error) { + var err error + req, err := client.getChatCompletionsWithAzureExtensionsCreateRequest(ctx, body, options) + if err != nil { + return GetChatCompletionsWithAzureExtensionsResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return GetChatCompletionsWithAzureExtensionsResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK) { + err = client.newError(httpResp) + return GetChatCompletionsWithAzureExtensionsResponse{}, err + } + resp, err := client.getChatCompletionsWithAzureExtensionsHandleResponse(httpResp) + return resp, err +} + +// getChatCompletionsWithAzureExtensionsCreateRequest creates the getChatCompletionsWithAzureExtensions request. +func (client *Client) getChatCompletionsWithAzureExtensionsCreateRequest(ctx context.Context, body ChatCompletionsOptions, options *GetChatCompletionsWithAzureExtensionsOptions) (*policy.Request, error) { + urlPath := "extensions/chat/completions" + req, err := runtime.NewRequest(ctx, http.MethodPost, client.formatURL(urlPath, getDeployment(body))) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2023-08-01-preview") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + if err := runtime.MarshalAsJSON(req, body); err != nil { + return nil, err + } + return req, nil +} + +// getChatCompletionsWithAzureExtensionsHandleResponse handles the getChatCompletionsWithAzureExtensions response. +func (client *Client) getChatCompletionsWithAzureExtensionsHandleResponse(resp *http.Response) (GetChatCompletionsWithAzureExtensionsResponse, error) { + result := GetChatCompletionsWithAzureExtensionsResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.ChatCompletions); err != nil { + return GetChatCompletionsWithAzureExtensionsResponse{}, err + } + return result, nil +} + // GetCompletions - Gets completions for the provided input prompts. Completions support a wide variety of tasks and generate // text that continues from or "completes" provided prompt data. // If the operation fails it returns an *azcore.ResponseError type. // -// Generated from API version 2023-07-01-preview +// Generated from API version 2023-08-01-preview // - options - GetCompletionsOptions contains the optional parameters for the Client.GetCompletions method. func (client *Client) GetCompletions(ctx context.Context, body CompletionsOptions, options *GetCompletionsOptions) (GetCompletionsResponse, error) { var err error @@ -163,7 +215,7 @@ func (client *Client) getCompletionsCreateRequest(ctx context.Context, body Comp return nil, err } reqQP := req.Raw().URL.Query() - reqQP.Set("api-version", "2023-07-01-preview") + reqQP.Set("api-version", "2023-08-01-preview") req.Raw().URL.RawQuery = reqQP.Encode() req.Raw().Header["Accept"] = []string{"application/json"} if err := runtime.MarshalAsJSON(req, body); err != nil { @@ -184,7 +236,7 @@ func (client *Client) getCompletionsHandleResponse(resp *http.Response) (GetComp // GetEmbeddings - Return the embeddings for a given prompt. // If the operation fails it returns an *azcore.ResponseError type. // -// Generated from API version 2023-07-01-preview +// Generated from API version 2023-08-01-preview // - options - GetEmbeddingsOptions contains the optional parameters for the Client.GetEmbeddings method. func (client *Client) GetEmbeddings(ctx context.Context, body EmbeddingsOptions, options *GetEmbeddingsOptions) (GetEmbeddingsResponse, error) { var err error @@ -212,7 +264,7 @@ func (client *Client) getEmbeddingsCreateRequest(ctx context.Context, body Embed return nil, err } reqQP := req.Raw().URL.Query() - reqQP.Set("api-version", "2023-07-01-preview") + reqQP.Set("api-version", "2023-08-01-preview") req.Raw().URL.RawQuery = reqQP.Encode() req.Raw().Header["Accept"] = []string{"application/json"} if err := runtime.MarshalAsJSON(req, body); err != nil { diff --git a/sdk/ai/azopenai/client_chat_completions_extensions_test.go b/sdk/ai/azopenai/client_chat_completions_extensions_test.go new file mode 100644 index 0000000000..ce1d8f7062 --- /dev/null +++ b/sdk/ai/azopenai/client_chat_completions_extensions_test.go @@ -0,0 +1,102 @@ +//go:build go1.18 +// +build go1.18 + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +package azopenai_test + +import ( + "context" + "errors" + "io" + "testing" + + "github.com/Azure/azure-sdk-for-go/sdk/ai/azopenai" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/stretchr/testify/require" +) + +func TestChatCompletions_extensions_bringYourOwnData(t *testing.T) { + client := newAzureOpenAIClientForTest(t, azureOpenAI) + + resp, err := client.GetChatCompletions(context.Background(), azopenai.ChatCompletionsOptions{ + Messages: []azopenai.ChatMessage{ + {Content: to.Ptr("What does PR complete mean?"), Role: to.Ptr(azopenai.ChatRoleUser)}, + }, + MaxTokens: to.Ptr[int32](512), + AzureExtensionsOptions: &azopenai.AzureChatExtensionOptions{ + Extensions: []azopenai.AzureChatExtensionConfiguration{ + { + Type: to.Ptr(azopenai.AzureChatExtensionTypeAzureCognitiveSearch), + Parameters: azureOpenAI.Cognitive, + }, + }, + }, + Deployment: "gpt-4", + }, nil) + require.NoError(t, err) + + // when you BYOD you get some extra content showing you metadata/info from the external + // data source. + msgContext := resp.Choices[0].Message.Context + require.NotEmpty(t, msgContext.Messages[0].Content) + require.Equal(t, azopenai.ChatRoleTool, *msgContext.Messages[0].Role) + + require.NotEmpty(t, *resp.Choices[0].Message.Content) + require.Equal(t, azopenai.CompletionsFinishReasonStop, *resp.Choices[0].FinishReason) +} + +func TestChatExtensionsStreaming_extensions_bringYourOwnData(t *testing.T) { + client := newAzureOpenAIClientForTest(t, azureOpenAI) + + streamResp, err := client.GetChatCompletionsStream(context.Background(), azopenai.ChatCompletionsOptions{ + Messages: []azopenai.ChatMessage{ + {Content: to.Ptr("What does PR complete mean?"), Role: to.Ptr(azopenai.ChatRoleUser)}, + }, + MaxTokens: to.Ptr[int32](512), + AzureExtensionsOptions: &azopenai.AzureChatExtensionOptions{ + Extensions: []azopenai.AzureChatExtensionConfiguration{ + { + Type: to.Ptr(azopenai.AzureChatExtensionTypeAzureCognitiveSearch), + Parameters: azureOpenAI.Cognitive, + }, + }, + }, + Deployment: "gpt-4", + }, nil) + + require.NoError(t, err) + defer streamResp.ChatCompletionsStream.Close() + + text := "" + + first := false + + for { + event, err := streamResp.ChatCompletionsStream.Read() + + if errors.Is(err, io.EOF) { + break + } + + require.NoError(t, err) + + if first { + // when you BYOD you get some extra content showing you metadata/info from the external + // data source. + first = false + msgContext := event.Choices[0].Message.Context + require.NotEmpty(t, msgContext.Messages[0].Content) + require.Equal(t, azopenai.ChatRoleTool, *msgContext.Messages[0].Role) + } + + for _, choice := range event.Choices { + if choice.Delta != nil && choice.Delta.Content != nil { + text += *choice.Delta.Content + } + } + } + + require.NotEmpty(t, text) +} diff --git a/sdk/ai/azopenai/client_shared_test.go b/sdk/ai/azopenai/client_shared_test.go index e40a40208b..8c5eb56f78 100644 --- a/sdk/ai/azopenai/client_shared_test.go +++ b/sdk/ai/azopenai/client_shared_test.go @@ -16,6 +16,7 @@ import ( "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/policy" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" "github.com/Azure/azure-sdk-for-go/sdk/internal/recording" "github.com/joho/godotenv" "github.com/stretchr/testify/require" @@ -34,6 +35,8 @@ type testVars struct { ChatCompletions string // env: AOAI_CHAT_COMPLETIONS_MODEL_DEPLOYMENT, OPENAI_CHAT_COMPLETIONS_MODEL Embeddings string // env: AOAI_EMBEDDINGS_MODEL_DEPLOYMENT, OPENAI_EMBEDDINGS_MODEL Azure bool + + Cognitive azopenai.AzureCognitiveSearchChatExtensionConfiguration } func newTestVars(prefix string, isCanary bool) testVars { @@ -73,6 +76,12 @@ func newTestVars(prefix string, isCanary bool) testVars { Embeddings: getRequired(prefix + "_EMBEDDINGS_MODEL" + deplSuffix + canarySuffix), Azure: azure, + + Cognitive: azopenai.AzureCognitiveSearchChatExtensionConfiguration{ + Endpoint: to.Ptr(getRequired("COGNITIVE_SEARCH_API_ENDPOINT")), + IndexName: to.Ptr(getRequired("COGNITIVE_SEARCH_API_INDEX")), + Key: to.Ptr(getRequired("COGNITIVE_SEARCH_API_KEY")), + }, } if tv.Endpoint != "" && !strings.HasSuffix(tv.Endpoint, "/") { @@ -83,8 +92,10 @@ func newTestVars(prefix string, isCanary bool) testVars { return tv } -const fakeEndpoint = "https://recordedhost/" +const fakeEndpoint = "https://fake-recorded-host.microsoft.com/" const fakeAPIKey = "redacted" +const fakeCognitiveEndpoint = "https://fake-cognitive-endpoint.microsoft.com" +const fakeCognitiveIndexName = "index" func initEnvVars() { if recording.GetRecordMode() == recording.PlaybackMode { @@ -107,7 +118,13 @@ func initEnvVars() { openAI.ChatCompletions = "gpt-4-0613" openAI.Embeddings = "text-embedding-ada-002" - azureOpenAI.Embeddings = "embedding" + azureOpenAI.Embeddings = "text-embedding-ada-002" + + azureOpenAI.Cognitive = azopenai.AzureCognitiveSearchChatExtensionConfiguration{ + Endpoint: to.Ptr(fakeCognitiveEndpoint), + IndexName: to.Ptr(fakeCognitiveIndexName), + Key: to.Ptr(fakeAPIKey), + } } else { if err := godotenv.Load(); err != nil { fmt.Printf("Failed to load .env file: %s\n", err) @@ -147,6 +164,21 @@ func newRecordingTransporter(t *testing.T) policy.Transporter { err = recording.AddURISanitizer(fakeEndpoint, regexp.QuoteMeta(openAI.Endpoint), nil) require.NoError(t, err) } + + err = recording.AddGeneralRegexSanitizer( + fmt.Sprintf(`"endpoint": "%s"`, fakeCognitiveEndpoint), + fmt.Sprintf(`"endpoint":\s*"%s"`, *azureOpenAI.Cognitive.Endpoint), nil) + require.NoError(t, err) + + err = recording.AddGeneralRegexSanitizer( + fmt.Sprintf(`"indexName": "%s"`, fakeCognitiveIndexName), + fmt.Sprintf(`"indexName":\s*"%s"`, *azureOpenAI.Cognitive.IndexName), nil) + require.NoError(t, err) + + err = recording.AddGeneralRegexSanitizer( + fmt.Sprintf(`"key": "%s"`, fakeAPIKey), + fmt.Sprintf(`"key":\s*"%s"`, *azureOpenAI.Cognitive.Key), nil) + require.NoError(t, err) } t.Cleanup(func() { diff --git a/sdk/ai/azopenai/constants.go b/sdk/ai/azopenai/constants.go index 1c3925d7c9..8a08912bac 100644 --- a/sdk/ai/azopenai/constants.go +++ b/sdk/ai/azopenai/constants.go @@ -8,6 +8,62 @@ package azopenai +// AzureChatExtensionType - A representation of configuration data for a single Azure OpenAI chat extension. This will be +// used by a chat completions request that should use Azure OpenAI chat extensions to augment the response +// behavior. The use of this configuration is compatible only with Azure OpenAI. +type AzureChatExtensionType string + +const ( + // AzureChatExtensionTypeAzureCognitiveSearch enables the use of an Azure Cognitive Search index with chat completions. + // [AzureChatExtensionConfiguration.Parameter] should be of type [AzureCognitiveSearchChatExtensionConfiguration]. + AzureChatExtensionTypeAzureCognitiveSearch AzureChatExtensionType = "AzureCognitiveSearch" +) + +// PossibleAzureChatExtensionTypeValues returns the possible values for the AzureChatExtensionType const type. +func PossibleAzureChatExtensionTypeValues() []AzureChatExtensionType { + return []AzureChatExtensionType{ + AzureChatExtensionTypeAzureCognitiveSearch, + } +} + +// AzureCognitiveSearchChatExtensionConfigurationType - The type label to use when configuring Azure OpenAI chat extensions. +// This should typically not be changed from its default value for Azure Cognitive Search. +type AzureCognitiveSearchChatExtensionConfigurationType string + +const ( + AzureCognitiveSearchChatExtensionConfigurationTypeAzureCognitiveSearch AzureCognitiveSearchChatExtensionConfigurationType = "AzureCognitiveSearch" +) + +// PossibleAzureCognitiveSearchChatExtensionConfigurationTypeValues returns the possible values for the AzureCognitiveSearchChatExtensionConfigurationType const type. +func PossibleAzureCognitiveSearchChatExtensionConfigurationTypeValues() []AzureCognitiveSearchChatExtensionConfigurationType { + return []AzureCognitiveSearchChatExtensionConfigurationType{ + AzureCognitiveSearchChatExtensionConfigurationTypeAzureCognitiveSearch, + } +} + +// AzureCognitiveSearchQueryType - The type of Azure Cognitive Search retrieval query that should be executed when using it +// as an Azure OpenAI chat extension. +type AzureCognitiveSearchQueryType string + +const ( + AzureCognitiveSearchQueryTypeSemantic AzureCognitiveSearchQueryType = "semantic" + AzureCognitiveSearchQueryTypeSimple AzureCognitiveSearchQueryType = "simple" + AzureCognitiveSearchQueryTypeVector AzureCognitiveSearchQueryType = "vector" + AzureCognitiveSearchQueryTypeVectorSemanticHybrid AzureCognitiveSearchQueryType = "vectorSemanticHybrid" + AzureCognitiveSearchQueryTypeVectorSimpleHybrid AzureCognitiveSearchQueryType = "vectorSimpleHybrid" +) + +// PossibleAzureCognitiveSearchQueryTypeValues returns the possible values for the AzureCognitiveSearchQueryType const type. +func PossibleAzureCognitiveSearchQueryTypeValues() []AzureCognitiveSearchQueryType { + return []AzureCognitiveSearchQueryType{ + AzureCognitiveSearchQueryTypeSemantic, + AzureCognitiveSearchQueryTypeSimple, + AzureCognitiveSearchQueryTypeVector, + AzureCognitiveSearchQueryTypeVectorSemanticHybrid, + AzureCognitiveSearchQueryTypeVectorSimpleHybrid, + } +} + // azureOpenAIOperationState - The state of a job or item. type azureOpenAIOperationState string @@ -26,6 +82,7 @@ const ( ChatRoleAssistant ChatRole = "assistant" ChatRoleFunction ChatRole = "function" ChatRoleSystem ChatRole = "system" + ChatRoleTool ChatRole = "tool" ChatRoleUser ChatRole = "user" ) @@ -35,6 +92,7 @@ func PossibleChatRoleValues() []ChatRole { ChatRoleAssistant, ChatRoleFunction, ChatRoleSystem, + ChatRoleTool, ChatRoleUser, } } diff --git a/sdk/ai/azopenai/custom_client.go b/sdk/ai/azopenai/custom_client.go index 9fb7f6df0d..eacc751341 100644 --- a/sdk/ai/azopenai/custom_client.go +++ b/sdk/ai/azopenai/custom_client.go @@ -189,7 +189,14 @@ func (client *Client) GetCompletionsStream(ctx context.Context, body Completions // If the operation fails it returns an *azcore.ResponseError type. // - options - GetCompletionsOptions contains the optional parameters for the Client.GetCompletions method. func (client *Client) GetChatCompletionsStream(ctx context.Context, body ChatCompletionsOptions, options *GetChatCompletionsStreamOptions) (GetChatCompletionsStreamResponse, error) { - req, err := client.getChatCompletionsCreateRequest(ctx, body, &GetChatCompletionsOptions{}) + var req *policy.Request + var err error + + if hasAzureExtensions(body) { + req, err = client.getChatCompletionsWithAzureExtensionsCreateRequest(ctx, body, &GetChatCompletionsWithAzureExtensionsOptions{}) + } else { + req, err = client.getChatCompletionsCreateRequest(ctx, body, &GetChatCompletionsOptions{}) + } if err != nil { return GetChatCompletionsStreamResponse{}, err @@ -219,6 +226,24 @@ func (client *Client) GetChatCompletionsStream(ctx context.Context, body ChatCom }, 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. +func (client *Client) GetChatCompletions(ctx context.Context, body ChatCompletionsOptions, options *GetChatCompletionsOptions) (GetChatCompletionsResponse, error) { + if hasAzureExtensions(body) { + resp, err := client.getChatCompletionsWithAzureExtensions(ctx, body, nil) + + // convert + if err != nil { + return GetChatCompletionsResponse{}, err + } + + return GetChatCompletionsResponse(resp), nil + } else { + return client.getChatCompletions(ctx, body, nil) + } +} + func (client *Client) formatURL(path string, deploymentID string) string { switch path { // https://learn.microsoft.com/en-us/azure/cognitive-services/openai/reference#image-generation @@ -257,3 +282,7 @@ func getDeployment[T ChatCompletionsOptions | CompletionsOptions | EmbeddingsOpt return "" } } + +func hasAzureExtensions(body ChatCompletionsOptions) bool { + return body.AzureExtensionsOptions != nil && len(body.AzureExtensionsOptions.Extensions) > 0 +} diff --git a/sdk/ai/azopenai/custom_models.go b/sdk/ai/azopenai/custom_models.go index 8a639b6ecc..5a12cd336e 100644 --- a/sdk/ai/azopenai/custom_models.go +++ b/sdk/ai/azopenai/custom_models.go @@ -95,3 +95,9 @@ func newContentFilterResponseError(resp *http.Response) error { ContentFilterResults: envelope.Error.InnerError.FilterResult, } } + +// AzureChatExtensionOptions provides Azure specific options to extend ChatCompletions. +type AzureChatExtensionOptions struct { + // Extensions is a slice of extensions to the chat completions endpoint, like Azure Cognitive Search. + Extensions []AzureChatExtensionConfiguration +} diff --git a/sdk/ai/azopenai/example_client_createimage_test.go b/sdk/ai/azopenai/example_client_createimage_test.go index aaf3c8b3b5..dade7b5a9f 100644 --- a/sdk/ai/azopenai/example_client_createimage_test.go +++ b/sdk/ai/azopenai/example_client_createimage_test.go @@ -9,6 +9,7 @@ package azopenai_test import ( "context" "fmt" + "log" "net/http" "os" @@ -30,13 +31,15 @@ func ExampleClient_CreateImage() { keyCredential, err := azopenai.NewKeyCredential(azureOpenAIKey) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } client, err := azopenai.NewClientWithKeyCredential(azureOpenAIEndpoint, keyCredential, nil) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } resp, err := client.CreateImage(context.TODO(), azopenai.ImageGenerationOptions{ @@ -45,7 +48,8 @@ func ExampleClient_CreateImage() { }, nil) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } for _, generatedImage := range resp.Data { @@ -57,6 +61,7 @@ func ExampleClient_CreateImage() { if err != nil { // TODO: handle error + log.Fatalf("ERROR: %s", err) } fmt.Fprintf(os.Stderr, "Image generated, HEAD request on URL returned %d\n", resp.StatusCode) diff --git a/sdk/ai/azopenai/example_client_embeddings_test.go b/sdk/ai/azopenai/example_client_embeddings_test.go index 59a84337f3..6080dc3660 100644 --- a/sdk/ai/azopenai/example_client_embeddings_test.go +++ b/sdk/ai/azopenai/example_client_embeddings_test.go @@ -6,6 +6,7 @@ package azopenai_test import ( "context" "fmt" + "log" "os" "github.com/Azure/azure-sdk-for-go/sdk/ai/azopenai" @@ -26,7 +27,8 @@ func ExampleClient_GetEmbeddings() { keyCredential, err := azopenai.NewKeyCredential(azureOpenAIKey) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } // In Azure OpenAI you must deploy a model before you can use it in your client. For more information @@ -34,7 +36,8 @@ func ExampleClient_GetEmbeddings() { client, err := azopenai.NewClientWithKeyCredential(azureOpenAIEndpoint, keyCredential, nil) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } resp, err := client.GetEmbeddings(context.TODO(), azopenai.EmbeddingsOptions{ @@ -43,7 +46,8 @@ func ExampleClient_GetEmbeddings() { }, nil) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } for _, embed := range resp.Data { diff --git a/sdk/ai/azopenai/example_client_getchatcompletions_extensions_test.go b/sdk/ai/azopenai/example_client_getchatcompletions_extensions_test.go new file mode 100644 index 0000000000..fc027cdf77 --- /dev/null +++ b/sdk/ai/azopenai/example_client_getchatcompletions_extensions_test.go @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +package azopenai_test + +import ( + "context" + "fmt" + "log" + "os" + + "github.com/Azure/azure-sdk-for-go/sdk/ai/azopenai" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" +) + +// With Azure OpenAI you can integrate data you've already uploaded to an Azure Cognitive Search index. +// For more information about this feature see the article "[Azure OpenAI on your data]". +// +// [Azure OpenAI on your data]: https://learn.microsoft.com/azure/ai-services/openai/concepts/use-your-data +func ExampleClient_GetChatCompletions_bringYourOwnDataWithCognitiveSearch() { + azureOpenAIKey := os.Getenv("AOAI_API_KEY") + modelDeploymentID := os.Getenv("AOAI_CHAT_COMPLETIONS_MODEL_DEPLOYMENT") + + // Ex: "https://.openai.azure.com" + azureOpenAIEndpoint := os.Getenv("AOAI_ENDPOINT") + + // Azure Cognitive Search configuration + searchIndex := os.Getenv("COGNITIVE_SEARCH_API_INDEX") + searchEndpoint := os.Getenv("COGNITIVE_SEARCH_API_ENDPOINT") + searchAPIKey := os.Getenv("COGNITIVE_SEARCH_API_KEY") + + if azureOpenAIKey == "" || modelDeploymentID == "" || azureOpenAIEndpoint == "" || searchIndex == "" || searchEndpoint == "" || searchAPIKey == "" { + fmt.Fprintf(os.Stderr, "Skipping example, environment variables missing\n") + return + } + + keyCredential, err := azopenai.NewKeyCredential(azureOpenAIKey) + + if err != nil { + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) + } + + // In Azure OpenAI you must deploy a model before you can use it in your client. For more information + // see here: https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource + client, err := azopenai.NewClientWithKeyCredential(azureOpenAIEndpoint, keyCredential, nil) + + if err != nil { + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) + } + + resp, err := client.GetChatCompletions(context.TODO(), azopenai.ChatCompletionsOptions{ + Messages: []azopenai.ChatMessage{ + {Content: to.Ptr("What are the differences between Azure Machine Learning and Azure AI services?"), Role: to.Ptr(azopenai.ChatRoleUser)}, + }, + MaxTokens: to.Ptr[int32](512), + AzureExtensionsOptions: &azopenai.AzureChatExtensionOptions{ + Extensions: []azopenai.AzureChatExtensionConfiguration{ + { + // This allows Azure OpenAI to use an Azure Cognitive Search index. + // + // > Because the model has access to, and can reference specific sources to support its responses, answers are not only based on its pretrained knowledge + // > but also on the latest information available in the designated data source. This grounding data also helps the model avoid generating responses + // > based on outdated or incorrect information. + // + // Quote from here: https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/use-your-data + Type: to.Ptr(azopenai.AzureChatExtensionTypeAzureCognitiveSearch), + Parameters: azopenai.AzureCognitiveSearchChatExtensionConfiguration{ + Endpoint: &searchEndpoint, + IndexName: &searchIndex, + Key: &searchAPIKey, + }, + }, + }, + }, + Deployment: "gpt-4", + }, nil) + + if err != nil { + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) + } + + // Contains contextual information from your Azure chat completion extensions, configured above in `AzureExtensionsOptions` + msgContext := resp.Choices[0].Message.Context + + fmt.Fprintf(os.Stderr, "Extensions Context Role: %s\nExtensions Context (length): %d\n", + *msgContext.Messages[0].Role, + len(*msgContext.Messages[0].Content)) + + fmt.Fprintf(os.Stderr, "ChatRole: %s\nChat content: %s\n", + *resp.Choices[0].Message.Role, + *resp.Choices[0].Message.Content, + ) + + // Output: +} diff --git a/sdk/ai/azopenai/example_client_getchatcompletions_test.go b/sdk/ai/azopenai/example_client_getchatcompletions_test.go index 6d451802ae..e53cbb8479 100644 --- a/sdk/ai/azopenai/example_client_getchatcompletions_test.go +++ b/sdk/ai/azopenai/example_client_getchatcompletions_test.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "io" + "log" "os" "github.com/Azure/azure-sdk-for-go/sdk/ai/azopenai" @@ -30,7 +31,8 @@ func ExampleClient_GetChatCompletions() { keyCredential, err := azopenai.NewKeyCredential(azureOpenAIKey) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } // In Azure OpenAI you must deploy a model before you can use it in your client. For more information @@ -38,7 +40,8 @@ func ExampleClient_GetChatCompletions() { client, err := azopenai.NewClientWithKeyCredential(azureOpenAIEndpoint, keyCredential, nil) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } // This is a conversation in progress. @@ -69,7 +72,8 @@ func ExampleClient_GetChatCompletions() { }, nil) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } for _, choice := range resp.Choices { @@ -99,7 +103,8 @@ func ExampleClient_GetChatCompletions_functions() { keyCredential, err := azopenai.NewKeyCredential(azureOpenAIKey) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } // In Azure OpenAI you must deploy a model before you can use it in your client. For more information @@ -107,10 +112,11 @@ func ExampleClient_GetChatCompletions_functions() { client, err := azopenai.NewClientWithKeyCredential(azureOpenAIEndpoint, keyCredential, nil) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } - resp, err := client.GetChatCompletions(context.Background(), azopenai.ChatCompletionsOptions{ + resp, err := client.GetChatCompletions(context.TODO(), azopenai.ChatCompletionsOptions{ Deployment: modelDeploymentID, Messages: []azopenai.ChatMessage{ { @@ -146,7 +152,8 @@ func ExampleClient_GetChatCompletions_functions() { }, nil) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } funcCall := resp.ChatCompletions.Choices[0].Message.FunctionCall @@ -163,7 +170,8 @@ func ExampleClient_GetChatCompletions_functions() { err = json.Unmarshal([]byte(*funcCall.Arguments), &funcParams) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } // Prints: @@ -188,7 +196,8 @@ func ExampleClient_GetChatCompletionsStream() { keyCredential, err := azopenai.NewKeyCredential(azureOpenAIKey) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } // In Azure OpenAI you must deploy a model before you can use it in your client. For more information @@ -196,7 +205,8 @@ func ExampleClient_GetChatCompletionsStream() { client, err := azopenai.NewClientWithKeyCredential(azureOpenAIEndpoint, keyCredential, nil) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } // This is a conversation in progress. @@ -226,7 +236,8 @@ func ExampleClient_GetChatCompletionsStream() { }, nil) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } defer resp.ChatCompletionsStream.Close() @@ -242,6 +253,7 @@ func ExampleClient_GetChatCompletionsStream() { if err != nil { // TODO: handle error + log.Fatalf("ERROR: %s", err) } for _, choice := range chatCompletions.Choices { diff --git a/sdk/ai/azopenai/example_client_getcompletions_test.go b/sdk/ai/azopenai/example_client_getcompletions_test.go index be794ae109..d8a06131ec 100644 --- a/sdk/ai/azopenai/example_client_getcompletions_test.go +++ b/sdk/ai/azopenai/example_client_getcompletions_test.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "io" + "log" "os" "github.com/Azure/azure-sdk-for-go/sdk/ai/azopenai" @@ -29,7 +30,8 @@ func ExampleClient_GetCompletions() { keyCredential, err := azopenai.NewKeyCredential(azureOpenAIKey) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } // In Azure OpenAI you must deploy a model before you can use it in your client. For more information @@ -37,7 +39,8 @@ func ExampleClient_GetCompletions() { client, err := azopenai.NewClientWithKeyCredential(azureOpenAIEndpoint, keyCredential, nil) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } resp, err := client.GetCompletions(context.TODO(), azopenai.CompletionsOptions{ @@ -48,7 +51,8 @@ func ExampleClient_GetCompletions() { }, nil) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } for _, choice := range resp.Choices { @@ -73,7 +77,8 @@ func ExampleClient_GetCompletionsStream() { keyCredential, err := azopenai.NewKeyCredential(azureOpenAIKey) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } // In Azure OpenAI you must deploy a model before you can use it in your client. For more information @@ -81,7 +86,8 @@ func ExampleClient_GetCompletionsStream() { client, err := azopenai.NewClientWithKeyCredential(azureOpenAIEndpoint, keyCredential, nil) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } resp, err := client.GetCompletionsStream(context.TODO(), azopenai.CompletionsOptions{ @@ -92,7 +98,8 @@ func ExampleClient_GetCompletionsStream() { }, nil) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } defer resp.CompletionsStream.Close() @@ -107,6 +114,7 @@ func ExampleClient_GetCompletionsStream() { if err != nil { // TODO: handle error + log.Fatalf("ERROR: %s", err) } for _, choice := range entry.Choices { diff --git a/sdk/ai/azopenai/example_client_test.go b/sdk/ai/azopenai/example_client_test.go index ad09cf6fca..75acda5498 100644 --- a/sdk/ai/azopenai/example_client_test.go +++ b/sdk/ai/azopenai/example_client_test.go @@ -4,6 +4,8 @@ package azopenai_test import ( + "log" + "github.com/Azure/azure-sdk-for-go/sdk/ai/azopenai" "github.com/Azure/azure-sdk-for-go/sdk/azidentity" ) @@ -12,7 +14,8 @@ func ExampleNewClientForOpenAI() { keyCredential, err := azopenai.NewKeyCredential("") if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } // NOTE: this constructor creates a client that connects to the public OpenAI endpoint. @@ -20,7 +23,8 @@ func ExampleNewClientForOpenAI() { client, err := azopenai.NewClientForOpenAI("https://api.openai.com/v1", keyCredential, nil) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } _ = client @@ -30,7 +34,8 @@ func ExampleNewClient() { dac, err := azidentity.NewDefaultAzureCredential(nil) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } // NOTE: this constructor creates a client that connects to an Azure OpenAI endpoint. @@ -38,7 +43,8 @@ func ExampleNewClient() { client, err := azopenai.NewClient("https://.openai.azure.com", dac, nil) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } _ = client @@ -48,7 +54,8 @@ func ExampleNewClientWithKeyCredential() { keyCredential, err := azopenai.NewKeyCredential("") if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } // NOTE: this constructor creates a client that connects to an Azure OpenAI endpoint. @@ -56,7 +63,8 @@ func ExampleNewClientWithKeyCredential() { client, err := azopenai.NewClientWithKeyCredential("https://.openai.azure.com", keyCredential, nil) if err != nil { - // TODO: handle error + // TODO: Update the following line with your application specific error handling logic + log.Fatalf("ERROR: %s", err) } _ = client diff --git a/sdk/ai/azopenai/models.go b/sdk/ai/azopenai/models.go index 8173ce3197..6fc762a712 100644 --- a/sdk/ai/azopenai/models.go +++ b/sdk/ai/azopenai/models.go @@ -8,6 +8,115 @@ package azopenai +import "time" + +// AzureChatExtensionConfiguration - A representation of configuration data for a single Azure OpenAI chat extension. This +// will be used by a chat completions request that should use Azure OpenAI chat extensions to augment the response +// behavior. The use of this configuration is compatible only with Azure OpenAI. +type AzureChatExtensionConfiguration struct { + + // REQUIRED; The label for the type of an Azure chat extension. This typically corresponds to a matching Azure resource. Azure + // chat extensions are only compatible with Azure OpenAI. + Type *AzureChatExtensionType + + // REQUIRED; The configuration payload used for the Azure chat extension. The structure payload details are specific to the + // extension being configured. Azure chat extensions are only compatible with Azure OpenAI. + Parameters any +} + +// AzureChatExtensionsMessageContext - A representation of the additional context information available when Azure OpenAI +// chat extensions are involved in the generation of a corresponding chat completions response. This context information +// is only populated when using an Azure OpenAI request configured to use a matching extension. +type AzureChatExtensionsMessageContext struct { + // The contextual message payload associated with the Azure chat extensions used for a chat completions request. These messages + // describe the data source retrievals, plugin invocations, and other + // intermediate steps taken in the course of generating a chat completions response that was augmented by capabilities from + // Azure OpenAI chat extensions. + Messages []ChatMessage +} + +// AzureCognitiveSearchChatExtensionConfiguration - A specific representation of configurable options for Azure Cognitive +// Search when using it as an Azure OpenAI chat extension. +type AzureCognitiveSearchChatExtensionConfiguration struct { + // REQUIRED; The absolute endpoint path for the Azure Cognitive Search resource to use. + Endpoint *string + + // REQUIRED; The name of the index to use as available in the referenced Azure Cognitive Search resource. + IndexName *string + + // REQUIRED; The API admin key to use with the specified Azure Cognitive Search endpoint. + Key *string + + // REQUIRED; The type label to use when configuring Azure OpenAI chat extensions. This should typically not be changed from + // its default value for Azure Cognitive Search. + Type *AzureCognitiveSearchChatExtensionConfigurationType + + // When using embeddings for search, specifies the resource URL from which embeddings should be retrieved. + EmbeddingEndpoint *string + + // When using embeddings, specifies the API key to use with the provided embeddings endpoint. + EmbeddingKey *string + + // Customized field mapping behavior to use when interacting with the search index. + FieldsMapping *AzureCognitiveSearchChatExtensionConfigurationFieldsMapping + + // Whether queries should be restricted to use of indexed data. + InScope *bool + + // The query type to use with Azure Cognitive Search. + QueryType *AzureCognitiveSearchQueryType + + // The additional semantic configuration for the query. + SemanticConfiguration *string + + // The configured top number of documents to feature for the configured query. + TopNDocuments *int32 +} + +// AzureCognitiveSearchChatExtensionConfigurationFieldsMapping - Customized field mapping behavior to use when interacting +// with the search index. +type AzureCognitiveSearchChatExtensionConfigurationFieldsMapping struct { + // The names of index fields that should be treated as content. + ContentFieldNames []string + + // The separator pattern that content fields should use. + ContentFieldSeparator *string + + // The name of the index field to use as a filepath. + FilepathField *string + + // The name of the index field to use as a title. + TitleField *string + + // The name of the index field to use as a URL. + URLField *string + + // The names of fields that represent vector data. + VectorFields []string +} + +// AzureCognitiveSearchIndexFieldMappingOptions - Optional settings to control how fields are processed when using a configured +// Azure Cognitive Search resource. +type AzureCognitiveSearchIndexFieldMappingOptions struct { + // The names of index fields that should be treated as content. + ContentFieldNames []string + + // The separator pattern that content fields should use. + ContentFieldSeparator *string + + // The name of the index field to use as a filepath. + FilepathField *string + + // The name of the index field to use as a title. + TitleField *string + + // The name of the index field to use as a URL. + URLField *string + + // The names of fields that represent vector data. + VectorFields []string +} + // azureCoreFoundationsError - The error object. type azureCoreFoundationsError struct { // REQUIRED; One of a server-defined set of error codes. @@ -83,7 +192,7 @@ type azureCoreFoundationsInnerErrorInnererror struct { // batchImageGenerationOperationResponse - A polling status update or final response payload for an image operation. type batchImageGenerationOperationResponse struct { // REQUIRED; A timestamp when this job or item was created (in unix epochs). - Created *int64 + Created *time.Time // REQUIRED; The ID of the operation. ID *string @@ -127,33 +236,38 @@ type ChatChoice struct { // it has been detected, as well as the severity level (verylow, low, medium, high-scale that determines the // intensity and risk level of harmful content) and if it has been filtered or not. type ChatChoiceContentFilterResults struct { - // REQUIRED; Describes language attacks or uses that include pejorative or discriminatory language with reference to a person - // or identity group on the basis of certain differentiating attributes of these groups + // Describes language attacks or uses that include pejorative or discriminatory language with reference to a person or identity + // group on the basis of certain differentiating attributes of these groups // including but not limited to race, ethnicity, nationality, gender identity and expression, sexual orientation, religion, // immigration status, ability status, personal appearance, and body size. Hate *ContentFilterResultsHate - // REQUIRED; Describes language related to physical actions intended to purposely hurt, injure, or damage one’s body, or kill - // oneself. + // Describes language related to physical actions intended to purposely hurt, injure, or damage one’s body, or kill oneself. SelfHarm *ContentFilterResultsSelfHarm - // REQUIRED; Describes language related to anatomical organs and genitals, romantic relationships, acts portrayed in erotic - // or affectionate terms, physical sexual acts, including those portrayed as an assault or a + // Describes language related to anatomical organs and genitals, romantic relationships, acts portrayed in erotic or affectionate + // terms, physical sexual acts, including those portrayed as an assault or a // forced sexual violent act against one’s will, prostitution, pornography, and abuse. Sexual *ContentFilterResultsSexual - // REQUIRED; Describes language related to physical actions intended to hurt, injure, damage, or kill someone or something; - // describes weapons, etc. + // Describes language related to physical actions intended to hurt, injure, damage, or kill someone or something; describes + // weapons, etc. Violence *ContentFilterResultsViolence } // ChatChoiceDelta - The delta message content for a streaming response. type ChatChoiceDelta struct { + // REQUIRED; The text associated with this message payload. + Content *string + // REQUIRED; The role associated with this message payload. Role *ChatRole - // The text associated with this message payload. - Content *string + // Additional context data associated with a chat message when requesting chat completions using compatible Azure OpenAI chat + // extensions. This includes information like the intermediate data source + // retrievals used to service a request. This context information is only populated when using Azure OpenAI with chat extensions + // capabilities configured. + Context *ChatMessageContext // The name and arguments of a function that should be called, as generated by the model. FunctionCall *ChatMessageFunctionCall @@ -166,11 +280,17 @@ type ChatChoiceDelta struct { // ChatChoiceMessage - The chat message for a given chat completions prompt. type ChatChoiceMessage struct { + // REQUIRED; The text associated with this message payload. + Content *string + // REQUIRED; The role associated with this message payload. Role *ChatRole - // The text associated with this message payload. - Content *string + // Additional context data associated with a chat message when requesting chat completions using compatible Azure OpenAI chat + // extensions. This includes information like the intermediate data source + // retrievals used to service a request. This context information is only populated when using Azure OpenAI with chat extensions + // capabilities configured. + Context *ChatMessageContext // The name and arguments of a function that should be called, as generated by the model. FunctionCall *ChatMessageFunctionCall @@ -191,7 +311,7 @@ type ChatCompletions struct { // REQUIRED; The first timestamp associated with generation activity for this completions response, represented as seconds // since the beginning of the Unix epoch of 00:00 on 1 Jan 1970. - Created *int32 + Created *time.Time // REQUIRED; A unique identifier associated with this chat completions response. ID *string @@ -212,6 +332,10 @@ type ChatCompletionsOptions struct { // assistant, followed by alternating messages between the User and Assistant roles. Messages []ChatMessage + // The configuration entries for Azure OpenAI chat extensions that use them. This additional specification is only compatible + // with Azure OpenAI. + AzureExtensionsOptions *AzureChatExtensionOptions + // A value that influences the probability of generated tokens appearing based on their cumulative frequency in generated // text. Positive values will make tokens less likely to appear as their frequency // increases and decrease the likelihood of the model repeating the same statements verbatim. @@ -272,11 +396,17 @@ type ChatCompletionsOptions struct { // ChatMessage - A single, role-attributed message within a chat completion interaction. type ChatMessage struct { + // REQUIRED; The text associated with this message payload. + Content *string + // REQUIRED; The role associated with this message payload. Role *ChatRole - // The text associated with this message payload. - Content *string + // Additional context data associated with a chat message when requesting chat completions using compatible Azure OpenAI chat + // extensions. This includes information like the intermediate data source + // retrievals used to service a request. This context information is only populated when using Azure OpenAI with chat extensions + // capabilities configured. + Context *ChatMessageContext // The name and arguments of a function that should be called, as generated by the model. FunctionCall *ChatMessageFunctionCall @@ -287,6 +417,18 @@ type ChatMessage struct { Name *string } +// ChatMessageContext - Additional context data associated with a chat message when requesting chat completions using compatible +// Azure OpenAI chat extensions. This includes information like the intermediate data source +// retrievals used to service a request. This context information is only populated when using Azure OpenAI with chat extensions +// capabilities configured. +type ChatMessageContext struct { + // The contextual message payload associated with the Azure chat extensions used for a chat completions request. These messages + // describe the data source retrievals, plugin invocations, and other + // intermediate steps taken in the course of generating a chat completions response that was augmented by capabilities from + // Azure OpenAI chat extensions. + Messages []ChatMessage +} + // ChatMessageFunctionCall - The name and arguments of a function that should be called, as generated by the model. type ChatMessageFunctionCall struct { // REQUIRED; The arguments to call the function with, as generated by the model in JSON format. Note that the model does not @@ -324,23 +466,22 @@ type Choice struct { // has been detected, as well as the severity level (verylow, low, medium, high-scale that determines the // intensity and risk level of harmful content) and if it has been filtered or not. type ChoiceContentFilterResults struct { - // REQUIRED; Describes language attacks or uses that include pejorative or discriminatory language with reference to a person - // or identity group on the basis of certain differentiating attributes of these groups + // Describes language attacks or uses that include pejorative or discriminatory language with reference to a person or identity + // group on the basis of certain differentiating attributes of these groups // including but not limited to race, ethnicity, nationality, gender identity and expression, sexual orientation, religion, // immigration status, ability status, personal appearance, and body size. Hate *ContentFilterResultsHate - // REQUIRED; Describes language related to physical actions intended to purposely hurt, injure, or damage one’s body, or kill - // oneself. + // Describes language related to physical actions intended to purposely hurt, injure, or damage one’s body, or kill oneself. SelfHarm *ContentFilterResultsSelfHarm - // REQUIRED; Describes language related to anatomical organs and genitals, romantic relationships, acts portrayed in erotic - // or affectionate terms, physical sexual acts, including those portrayed as an assault or a + // Describes language related to anatomical organs and genitals, romantic relationships, acts portrayed in erotic or affectionate + // terms, physical sexual acts, including those portrayed as an assault or a // forced sexual violent act against one’s will, prostitution, pornography, and abuse. Sexual *ContentFilterResultsSexual - // REQUIRED; Describes language related to physical actions intended to hurt, injure, damage, or kill someone or something; - // describes weapons, etc. + // Describes language related to physical actions intended to hurt, injure, damage, or kill someone or something; describes + // weapons, etc. Violence *ContentFilterResultsViolence } @@ -369,7 +510,7 @@ type Completions struct { // REQUIRED; The first timestamp associated with generation activity for this completions response, represented as seconds // since the beginning of the Unix epoch of 00:00 on 1 Jan 1970. - Created *int32 + Created *time.Time // REQUIRED; A unique identifier associated with this completions response. ID *string @@ -479,23 +620,22 @@ type CompletionsUsage struct { // ContentFilterResults - Information about the content filtering category, if it has been detected. type ContentFilterResults struct { - // REQUIRED; Describes language attacks or uses that include pejorative or discriminatory language with reference to a person - // or identity group on the basis of certain differentiating attributes of these groups + // Describes language attacks or uses that include pejorative or discriminatory language with reference to a person or identity + // group on the basis of certain differentiating attributes of these groups // including but not limited to race, ethnicity, nationality, gender identity and expression, sexual orientation, religion, // immigration status, ability status, personal appearance, and body size. Hate *ContentFilterResultsHate - // REQUIRED; Describes language related to physical actions intended to purposely hurt, injure, or damage one’s body, or kill - // oneself. + // Describes language related to physical actions intended to purposely hurt, injure, or damage one’s body, or kill oneself. SelfHarm *ContentFilterResultsSelfHarm - // REQUIRED; Describes language related to anatomical organs and genitals, romantic relationships, acts portrayed in erotic - // or affectionate terms, physical sexual acts, including those portrayed as an assault or a + // Describes language related to anatomical organs and genitals, romantic relationships, acts portrayed in erotic or affectionate + // terms, physical sexual acts, including those portrayed as an assault or a // forced sexual violent act against one’s will, prostitution, pornography, and abuse. Sexual *ContentFilterResultsSexual - // REQUIRED; Describes language related to physical actions intended to hurt, injure, damage, or kill someone or something; - // describes weapons, etc. + // Describes language related to physical actions intended to hurt, injure, damage, or kill someone or something; describes + // weapons, etc. Violence *ContentFilterResultsViolence } @@ -657,7 +797,7 @@ type ImageGenerationOptions struct { // ImageGenerations - The result of the operation if the operation succeeded. type ImageGenerations struct { // REQUIRED; A timestamp when this job or item was created (in unix epochs). - Created *int64 + Created *time.Time // REQUIRED; The images generated by the operator. Data []ImageGenerationsDataItem @@ -686,22 +826,21 @@ type PromptFilterResult struct { // PromptFilterResultContentFilterResults - Content filtering results for this prompt type PromptFilterResultContentFilterResults struct { - // REQUIRED; Describes language attacks or uses that include pejorative or discriminatory language with reference to a person - // or identity group on the basis of certain differentiating attributes of these groups + // Describes language attacks or uses that include pejorative or discriminatory language with reference to a person or identity + // group on the basis of certain differentiating attributes of these groups // including but not limited to race, ethnicity, nationality, gender identity and expression, sexual orientation, religion, // immigration status, ability status, personal appearance, and body size. Hate *ContentFilterResultsHate - // REQUIRED; Describes language related to physical actions intended to purposely hurt, injure, or damage one’s body, or kill - // oneself. + // Describes language related to physical actions intended to purposely hurt, injure, or damage one’s body, or kill oneself. SelfHarm *ContentFilterResultsSelfHarm - // REQUIRED; Describes language related to anatomical organs and genitals, romantic relationships, acts portrayed in erotic - // or affectionate terms, physical sexual acts, including those portrayed as an assault or a + // Describes language related to anatomical organs and genitals, romantic relationships, acts portrayed in erotic or affectionate + // terms, physical sexual acts, including those portrayed as an assault or a // forced sexual violent act against one’s will, prostitution, pornography, and abuse. Sexual *ContentFilterResultsSexual - // REQUIRED; Describes language related to physical actions intended to hurt, injure, damage, or kill someone or something; - // describes weapons, etc. + // Describes language related to physical actions intended to hurt, injure, damage, or kill someone or something; describes + // weapons, etc. Violence *ContentFilterResultsViolence } diff --git a/sdk/ai/azopenai/models_serde.go b/sdk/ai/azopenai/models_serde.go index ed13ca5ebf..6c3cab72cd 100644 --- a/sdk/ai/azopenai/models_serde.go +++ b/sdk/ai/azopenai/models_serde.go @@ -16,6 +16,225 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore" ) +// MarshalJSON implements the json.Marshaller interface for type AzureChatExtensionConfiguration. +func (a AzureChatExtensionConfiguration) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populateAny(objectMap, "parameters", a.Parameters) + populate(objectMap, "type", a.Type) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type AzureChatExtensionConfiguration. +func (a *AzureChatExtensionConfiguration) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "parameters": + err = unpopulate(val, "Parameters", &a.Parameters) + delete(rawMsg, key) + case "type": + err = unpopulate(val, "Type", &a.Type) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type AzureChatExtensionsMessageContext. +func (a AzureChatExtensionsMessageContext) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "messages", a.Messages) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type AzureChatExtensionsMessageContext. +func (a *AzureChatExtensionsMessageContext) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "messages": + err = unpopulate(val, "Messages", &a.Messages) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type AzureCognitiveSearchChatExtensionConfiguration. +func (a AzureCognitiveSearchChatExtensionConfiguration) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "embeddingEndpoint", a.EmbeddingEndpoint) + populate(objectMap, "embeddingKey", a.EmbeddingKey) + populate(objectMap, "endpoint", a.Endpoint) + populate(objectMap, "fieldsMapping", a.FieldsMapping) + populate(objectMap, "inScope", a.InScope) + populate(objectMap, "indexName", a.IndexName) + populate(objectMap, "key", a.Key) + populate(objectMap, "queryType", a.QueryType) + populate(objectMap, "semanticConfiguration", a.SemanticConfiguration) + populate(objectMap, "topNDocuments", a.TopNDocuments) + populate(objectMap, "type", a.Type) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type AzureCognitiveSearchChatExtensionConfiguration. +func (a *AzureCognitiveSearchChatExtensionConfiguration) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "embeddingEndpoint": + err = unpopulate(val, "EmbeddingEndpoint", &a.EmbeddingEndpoint) + delete(rawMsg, key) + case "embeddingKey": + err = unpopulate(val, "EmbeddingKey", &a.EmbeddingKey) + delete(rawMsg, key) + case "endpoint": + err = unpopulate(val, "Endpoint", &a.Endpoint) + delete(rawMsg, key) + case "fieldsMapping": + err = unpopulate(val, "FieldsMapping", &a.FieldsMapping) + delete(rawMsg, key) + case "inScope": + err = unpopulate(val, "InScope", &a.InScope) + delete(rawMsg, key) + case "indexName": + err = unpopulate(val, "IndexName", &a.IndexName) + delete(rawMsg, key) + case "key": + err = unpopulate(val, "Key", &a.Key) + delete(rawMsg, key) + case "queryType": + err = unpopulate(val, "QueryType", &a.QueryType) + delete(rawMsg, key) + case "semanticConfiguration": + err = unpopulate(val, "SemanticConfiguration", &a.SemanticConfiguration) + delete(rawMsg, key) + case "topNDocuments": + err = unpopulate(val, "TopNDocuments", &a.TopNDocuments) + delete(rawMsg, key) + case "type": + err = unpopulate(val, "Type", &a.Type) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type AzureCognitiveSearchChatExtensionConfigurationFieldsMapping. +func (a AzureCognitiveSearchChatExtensionConfigurationFieldsMapping) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "contentFieldNames", a.ContentFieldNames) + populate(objectMap, "contentFieldSeparator", a.ContentFieldSeparator) + populate(objectMap, "filepathField", a.FilepathField) + populate(objectMap, "titleField", a.TitleField) + populate(objectMap, "urlField", a.URLField) + populate(objectMap, "vectorFields", a.VectorFields) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type AzureCognitiveSearchChatExtensionConfigurationFieldsMapping. +func (a *AzureCognitiveSearchChatExtensionConfigurationFieldsMapping) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "contentFieldNames": + err = unpopulate(val, "ContentFieldNames", &a.ContentFieldNames) + delete(rawMsg, key) + case "contentFieldSeparator": + err = unpopulate(val, "ContentFieldSeparator", &a.ContentFieldSeparator) + delete(rawMsg, key) + case "filepathField": + err = unpopulate(val, "FilepathField", &a.FilepathField) + delete(rawMsg, key) + case "titleField": + err = unpopulate(val, "TitleField", &a.TitleField) + delete(rawMsg, key) + case "urlField": + err = unpopulate(val, "URLField", &a.URLField) + delete(rawMsg, key) + case "vectorFields": + err = unpopulate(val, "VectorFields", &a.VectorFields) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type AzureCognitiveSearchIndexFieldMappingOptions. +func (a AzureCognitiveSearchIndexFieldMappingOptions) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "contentFieldNames", a.ContentFieldNames) + populate(objectMap, "contentFieldSeparator", a.ContentFieldSeparator) + populate(objectMap, "filepathField", a.FilepathField) + populate(objectMap, "titleField", a.TitleField) + populate(objectMap, "urlField", a.URLField) + populate(objectMap, "vectorFields", a.VectorFields) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type AzureCognitiveSearchIndexFieldMappingOptions. +func (a *AzureCognitiveSearchIndexFieldMappingOptions) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "contentFieldNames": + err = unpopulate(val, "ContentFieldNames", &a.ContentFieldNames) + delete(rawMsg, key) + case "contentFieldSeparator": + err = unpopulate(val, "ContentFieldSeparator", &a.ContentFieldSeparator) + delete(rawMsg, key) + case "filepathField": + err = unpopulate(val, "FilepathField", &a.FilepathField) + delete(rawMsg, key) + case "titleField": + err = unpopulate(val, "TitleField", &a.TitleField) + delete(rawMsg, key) + case "urlField": + err = unpopulate(val, "URLField", &a.URLField) + delete(rawMsg, key) + case "vectorFields": + err = unpopulate(val, "VectorFields", &a.VectorFields) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + } + return nil +} + // MarshalJSON implements the json.Marshaller interface for type azureCoreFoundationsError. func (a azureCoreFoundationsError) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) @@ -225,7 +444,7 @@ func (a *azureCoreFoundationsInnerErrorInnererror) UnmarshalJSON(data []byte) er // MarshalJSON implements the json.Marshaller interface for type batchImageGenerationOperationResponse. func (b batchImageGenerationOperationResponse) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) - populate(objectMap, "created", b.Created) + populateTimeUnix(objectMap, "created", b.Created) populate(objectMap, "error", b.Error) populate(objectMap, "expires", b.Expires) populate(objectMap, "id", b.ID) @@ -244,7 +463,7 @@ func (b *batchImageGenerationOperationResponse) UnmarshalJSON(data []byte) error var err error switch key { case "created": - err = unpopulate(val, "Created", &b.Created) + err = unpopulateTimeUnix(val, "Created", &b.Created) delete(rawMsg, key) case "error": err = unpopulate(val, "Error", &b.Error) @@ -355,6 +574,7 @@ func (c *ChatChoiceContentFilterResults) UnmarshalJSON(data []byte) error { func (c ChatChoiceDelta) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) populate(objectMap, "content", c.Content) + populate(objectMap, "context", c.Context) populate(objectMap, "function_call", c.FunctionCall) populate(objectMap, "name", c.Name) populate(objectMap, "role", c.Role) @@ -373,6 +593,9 @@ func (c *ChatChoiceDelta) UnmarshalJSON(data []byte) error { case "content": err = unpopulate(val, "Content", &c.Content) delete(rawMsg, key) + case "context": + err = unpopulate(val, "Context", &c.Context) + delete(rawMsg, key) case "function_call": err = unpopulate(val, "FunctionCall", &c.FunctionCall) delete(rawMsg, key) @@ -394,6 +617,7 @@ func (c *ChatChoiceDelta) UnmarshalJSON(data []byte) error { func (c ChatChoiceMessage) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) populate(objectMap, "content", c.Content) + populate(objectMap, "context", c.Context) populate(objectMap, "function_call", c.FunctionCall) populate(objectMap, "name", c.Name) populate(objectMap, "role", c.Role) @@ -412,6 +636,9 @@ func (c *ChatChoiceMessage) UnmarshalJSON(data []byte) error { case "content": err = unpopulate(val, "Content", &c.Content) delete(rawMsg, key) + case "context": + err = unpopulate(val, "Context", &c.Context) + delete(rawMsg, key) case "function_call": err = unpopulate(val, "FunctionCall", &c.FunctionCall) delete(rawMsg, key) @@ -433,7 +660,7 @@ func (c *ChatChoiceMessage) UnmarshalJSON(data []byte) error { func (c ChatCompletions) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) populate(objectMap, "choices", c.Choices) - populate(objectMap, "created", c.Created) + populateTimeUnix(objectMap, "created", c.Created) populate(objectMap, "id", c.ID) populate(objectMap, "prompt_annotations", c.PromptAnnotations) populate(objectMap, "usage", c.Usage) @@ -453,7 +680,7 @@ func (c *ChatCompletions) UnmarshalJSON(data []byte) error { err = unpopulate(val, "Choices", &c.Choices) delete(rawMsg, key) case "created": - err = unpopulate(val, "Created", &c.Created) + err = unpopulateTimeUnix(val, "Created", &c.Created) delete(rawMsg, key) case "id": err = unpopulate(val, "ID", &c.ID) @@ -475,6 +702,9 @@ func (c *ChatCompletions) UnmarshalJSON(data []byte) error { // MarshalJSON implements the json.Marshaller interface for type ChatCompletionsOptions. func (c ChatCompletionsOptions) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) + if c.AzureExtensionsOptions != nil { + populate(objectMap, "dataSources", c.AzureExtensionsOptions.Extensions) + } populate(objectMap, "frequency_penalty", c.FrequencyPenalty) populate(objectMap, "function_call", c.FunctionCall) populate(objectMap, "functions", c.Functions) @@ -500,6 +730,10 @@ func (c *ChatCompletionsOptions) UnmarshalJSON(data []byte) error { for key, val := range rawMsg { var err error switch key { + case "dataSources": + c.AzureExtensionsOptions = &AzureChatExtensionOptions{} + err = unpopulate(val, "DataSources", &c.AzureExtensionsOptions.Extensions) + delete(rawMsg, key) case "frequency_penalty": err = unpopulate(val, "FrequencyPenalty", &c.FrequencyPenalty) delete(rawMsg, key) @@ -551,6 +785,7 @@ func (c *ChatCompletionsOptions) UnmarshalJSON(data []byte) error { func (c ChatMessage) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) populate(objectMap, "content", c.Content) + populate(objectMap, "context", c.Context) populate(objectMap, "function_call", c.FunctionCall) populate(objectMap, "name", c.Name) populate(objectMap, "role", c.Role) @@ -569,6 +804,9 @@ func (c *ChatMessage) UnmarshalJSON(data []byte) error { case "content": err = unpopulate(val, "Content", &c.Content) delete(rawMsg, key) + case "context": + err = unpopulate(val, "Context", &c.Context) + delete(rawMsg, key) case "function_call": err = unpopulate(val, "FunctionCall", &c.FunctionCall) delete(rawMsg, key) @@ -586,6 +824,33 @@ func (c *ChatMessage) UnmarshalJSON(data []byte) error { return nil } +// MarshalJSON implements the json.Marshaller interface for type ChatMessageContext. +func (c ChatMessageContext) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "messages", c.Messages) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type ChatMessageContext. +func (c *ChatMessageContext) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", c, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "messages": + err = unpopulate(val, "Messages", &c.Messages) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", c, err) + } + } + return nil +} + // MarshalJSON implements the json.Marshaller interface for type ChatMessageFunctionCall. func (c ChatMessageFunctionCall) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) @@ -742,7 +1007,7 @@ func (c *ChoiceLogProbs) UnmarshalJSON(data []byte) error { func (c Completions) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) populate(objectMap, "choices", c.Choices) - populate(objectMap, "created", c.Created) + populateTimeUnix(objectMap, "created", c.Created) populate(objectMap, "id", c.ID) populate(objectMap, "prompt_annotations", c.PromptAnnotations) populate(objectMap, "usage", c.Usage) @@ -762,7 +1027,7 @@ func (c *Completions) UnmarshalJSON(data []byte) error { err = unpopulate(val, "Choices", &c.Choices) delete(rawMsg, key) case "created": - err = unpopulate(val, "Created", &c.Created) + err = unpopulateTimeUnix(val, "Created", &c.Created) delete(rawMsg, key) case "id": err = unpopulate(val, "ID", &c.ID) @@ -1422,7 +1687,7 @@ func (i *ImageGenerationOptions) UnmarshalJSON(data []byte) error { // MarshalJSON implements the json.Marshaller interface for type ImageGenerations. func (i ImageGenerations) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) - populate(objectMap, "created", i.Created) + populateTimeUnix(objectMap, "created", i.Created) populateAny(objectMap, "data", i.Data) return json.Marshal(objectMap) } @@ -1437,7 +1702,7 @@ func (i *ImageGenerations) UnmarshalJSON(data []byte) error { var err error switch key { case "created": - err = unpopulate(val, "Created", &i.Created) + err = unpopulateTimeUnix(val, "Created", &i.Created) delete(rawMsg, key) case "data": err = unpopulate(val, "Data", &i.Data) diff --git a/sdk/ai/azopenai/options.go b/sdk/ai/azopenai/options.go index 9b28464299..3dead1bb19 100644 --- a/sdk/ai/azopenai/options.go +++ b/sdk/ai/azopenai/options.go @@ -20,6 +20,12 @@ type GetChatCompletionsOptions struct { // placeholder for future optional parameters } +// GetChatCompletionsWithAzureExtensionsOptions contains the optional parameters for the Client.GetChatCompletionsWithAzureExtensions +// method. +type GetChatCompletionsWithAzureExtensionsOptions struct { + // placeholder for future optional parameters +} + // GetCompletionsOptions contains the optional parameters for the Client.GetCompletions method. type GetCompletionsOptions struct { // placeholder for future optional parameters diff --git a/sdk/ai/azopenai/response_types.go b/sdk/ai/azopenai/response_types.go index 0cf6c589ff..b69aac0601 100644 --- a/sdk/ai/azopenai/response_types.go +++ b/sdk/ai/azopenai/response_types.go @@ -22,6 +22,14 @@ type GetChatCompletionsResponse struct { ChatCompletions } +// GetChatCompletionsWithAzureExtensionsResponse contains the response from method Client.GetChatCompletionsWithAzureExtensions. +type GetChatCompletionsWithAzureExtensionsResponse struct { + // Representation of the response data from a chat completions request. + // Completions support a wide variety of tasks and generate text that continues from or "completes" + // provided prompt data. + ChatCompletions +} + // GetCompletionsResponse contains the response from method Client.GetCompletions. type GetCompletionsResponse struct { // Representation of the response data from a completions request. diff --git a/sdk/ai/azopenai/testdata/tsp-location.yaml b/sdk/ai/azopenai/testdata/tsp-location.yaml index f621a75951..b8435e327e 100644 --- a/sdk/ai/azopenai/testdata/tsp-location.yaml +++ b/sdk/ai/azopenai/testdata/tsp-location.yaml @@ -1,4 +1,4 @@ #location: https://github.com/Azure/azure-rest-api-specs/tree/1393b6e34d7370733e3e2236c4df686280a96f36/specification/cognitiveservices/OpenAI.Inference directory: specification/cognitiveservices/OpenAI.Inference -commit: 812c8a0322c016efec774d5682797d5a40336131 -repo: Azure/azure-rest-api-specs \ No newline at end of file +commit: b646a42aa3b7a0ce488d05f1724827ea41d12cf1 +repo: Azure/azure-rest-api-specs diff --git a/sdk/ai/azopenai/time_unix.go b/sdk/ai/azopenai/time_unix.go new file mode 100644 index 0000000000..a07a9be4ba --- /dev/null +++ b/sdk/ai/azopenai/time_unix.go @@ -0,0 +1,62 @@ +//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. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package azopenai + +import ( + "encoding/json" + "fmt" + "reflect" + "strings" + "time" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" +) + +type timeUnix time.Time + +func (t timeUnix) MarshalJSON() ([]byte, error) { + return json.Marshal(time.Time(t).Unix()) +} + +func (t *timeUnix) UnmarshalJSON(data []byte) error { + var seconds int64 + if err := json.Unmarshal(data, &seconds); err != nil { + return err + } + *t = timeUnix(time.Unix(seconds, 0)) + return nil +} + +func (t timeUnix) String() string { + return fmt.Sprintf("%d", time.Time(t).Unix()) +} + +func populateTimeUnix(m map[string]any, k string, t *time.Time) { + if t == nil { + return + } else if azcore.IsNullValue(t) { + m[k] = nil + return + } else if reflect.ValueOf(t).IsNil() { + return + } + m[k] = (*timeUnix)(t) +} + +func unpopulateTimeUnix(data json.RawMessage, fn string, t **time.Time) error { + if data == nil || strings.EqualFold(string(data), "null") { + return nil + } + var aux timeUnix + if err := json.Unmarshal(data, &aux); err != nil { + return fmt.Errorf("struct field %s: %v", fn, err) + } + *t = (*time.Time)(&aux) + return nil +} diff --git a/sdk/ai/azopenai/version.go b/sdk/ai/azopenai/version.go index 872289cf92..fb2f6f5d7a 100644 --- a/sdk/ai/azopenai/version.go +++ b/sdk/ai/azopenai/version.go @@ -7,5 +7,5 @@ package azopenai const ( - version = "v0.1.2" + version = "v0.2.0" )