diff --git a/sdk/cognitiveservices/azopenai/CHANGELOG.md b/sdk/cognitiveservices/azopenai/CHANGELOG.md index 0c0dbdbd03fc..953d18b9be3f 100644 --- a/sdk/cognitiveservices/azopenai/CHANGELOG.md +++ b/sdk/cognitiveservices/azopenai/CHANGELOG.md @@ -1,5 +1,5 @@ # Release History -## 0.1.0 (2023-07-19) +## 0.1.0 (2023-07-20) * Initial release of the `azopenai` library diff --git a/sdk/cognitiveservices/azopenai/assets.json b/sdk/cognitiveservices/azopenai/assets.json index 8153da5c9cc9..80e73970708d 100644 --- a/sdk/cognitiveservices/azopenai/assets.json +++ b/sdk/cognitiveservices/azopenai/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "go", "TagPrefix": "go/cognitiveservices/azopenai", - "Tag": "go/cognitiveservices/azopenai_e8362ae205" + "Tag": "go/cognitiveservices/azopenai_8fdad86997" } diff --git a/sdk/cognitiveservices/azopenai/autorest.md b/sdk/cognitiveservices/azopenai/autorest.md index ad7af1f0a84e..a52c7a6e707e 100644 --- a/sdk/cognitiveservices/azopenai/autorest.md +++ b/sdk/cognitiveservices/azopenai/autorest.md @@ -172,7 +172,9 @@ directive: # allow interception of formatting the URL path - from: client.go where: $ - transform: return $.replace(/runtime\.JoinPaths\(client.endpoint, urlPath\)/g, "client.formatURL(urlPath)"); + transform: | + return $ + .replace(/runtime\.JoinPaths\(client.endpoint, urlPath\)/g, "client.formatURL(urlPath, getDeploymentID(body))"); # Some ImageGenerations hackery to represent the ImageLocation/ImagePayload polymorphism. # - Remove the auto-generated ImageGenerationsDataItem. @@ -276,4 +278,20 @@ directive: - from: client.go where: $ transform: return $.replace(/runtime\.NewResponseError/sg, "client.newError"); + + # + # rename `Model` to `DeploymentID` + # + - from: models.go + where: $ + transform: | + return $ + .replace(/\/\/ The model name.*?Model \*string/sg, "// DeploymentID specifies the name of the deployment (for Azure OpenAI) or model (for OpenAI) to use for this request.\nDeploymentID string"); + + - from: models_serde.go + where: $ + transform: | + return $ + .replace(/populate\(objectMap, "model", (c|e).Model\)/g, 'populate(objectMap, "model", &$1.DeploymentID)') + .replace(/err = unpopulate\(val, "Model", &(c|e).Model\)/g, 'err = unpopulate(val, "Model", &$1.DeploymentID)'); ``` diff --git a/sdk/cognitiveservices/azopenai/client.go b/sdk/cognitiveservices/azopenai/client.go index 3fccca28a9f6..ad5759b38033 100644 --- a/sdk/cognitiveservices/azopenai/client.go +++ b/sdk/cognitiveservices/azopenai/client.go @@ -67,7 +67,7 @@ func (client *Client) azureBatchImageGenerationInternal(ctx context.Context, bod // azureBatchImageGenerationInternalCreateRequest creates the AzureBatchImageGenerationInternal request. func (client *Client) azureBatchImageGenerationInternalCreateRequest(ctx context.Context, body ImageGenerationOptions, options *beginAzureBatchImageGenerationOptions) (*policy.Request, error) { urlPath := "/images/generations:submit" - req, err := runtime.NewRequest(ctx, http.MethodPost, client.formatURL(urlPath)) + req, err := runtime.NewRequest(ctx, http.MethodPost, client.formatURL(urlPath, getDeploymentID(body))) if err != nil { return nil, err } @@ -108,7 +108,7 @@ func (client *Client) GetChatCompletions(ctx context.Context, body ChatCompletio // 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)) + req, err := runtime.NewRequest(ctx, http.MethodPost, client.formatURL(urlPath, getDeploymentID(body))) if err != nil { return nil, err } @@ -158,7 +158,7 @@ func (client *Client) GetCompletions(ctx context.Context, body CompletionsOption // getCompletionsCreateRequest creates the GetCompletions request. func (client *Client) getCompletionsCreateRequest(ctx context.Context, body CompletionsOptions, options *GetCompletionsOptions) (*policy.Request, error) { urlPath := "completions" - req, err := runtime.NewRequest(ctx, http.MethodPost, client.formatURL(urlPath)) + req, err := runtime.NewRequest(ctx, http.MethodPost, client.formatURL(urlPath, getDeploymentID(body))) if err != nil { return nil, err } @@ -207,7 +207,7 @@ func (client *Client) GetEmbeddings(ctx context.Context, body EmbeddingsOptions, // getEmbeddingsCreateRequest creates the GetEmbeddings request. func (client *Client) getEmbeddingsCreateRequest(ctx context.Context, body EmbeddingsOptions, options *GetEmbeddingsOptions) (*policy.Request, error) { urlPath := "embeddings" - req, err := runtime.NewRequest(ctx, http.MethodPost, client.formatURL(urlPath)) + req, err := runtime.NewRequest(ctx, http.MethodPost, client.formatURL(urlPath, getDeploymentID(body))) if err != nil { return nil, err } diff --git a/sdk/cognitiveservices/azopenai/client_chat_completions_test.go b/sdk/cognitiveservices/azopenai/client_chat_completions_test.go index 98d6c8fd971a..1382f0986d91 100644 --- a/sdk/cognitiveservices/azopenai/client_chat_completions_test.go +++ b/sdk/cognitiveservices/azopenai/client_chat_completions_test.go @@ -22,34 +22,36 @@ import ( "github.com/stretchr/testify/require" ) -var chatCompletionsRequest = azopenai.ChatCompletionsOptions{ - Messages: []azopenai.ChatMessage{ - { - Role: to.Ptr(azopenai.ChatRole("user")), - Content: to.Ptr("Count to 10, with a comma between each number, no newlines and a period at the end. E.g., 1, 2, 3, ..."), +func newTestChatCompletionOptions(tv testVars) azopenai.ChatCompletionsOptions { + return azopenai.ChatCompletionsOptions{ + Messages: []azopenai.ChatMessage{ + { + Role: to.Ptr(azopenai.ChatRole("user")), + Content: to.Ptr("Count to 10, with a comma between each number, no newlines and a period at the end. E.g., 1, 2, 3, ..."), + }, }, - }, - MaxTokens: to.Ptr(int32(1024)), - Temperature: to.Ptr(float32(0.0)), - Model: &openAIChatCompletionsModel, + MaxTokens: to.Ptr(int32(1024)), + Temperature: to.Ptr(float32(0.0)), + DeploymentID: tv.ChatCompletions, + } } var expectedContent = "1, 2, 3, 4, 5, 6, 7, 8, 9, 10." var expectedRole = azopenai.ChatRoleAssistant func TestClient_GetChatCompletions(t *testing.T) { - cred, err := azopenai.NewKeyCredential(apiKey) + cred, err := azopenai.NewKeyCredential(azureOpenAI.APIKey) require.NoError(t, err) - chatClient, err := azopenai.NewClientWithKeyCredential(endpoint, cred, chatCompletionsModelDeployment, newClientOptionsForTest(t)) + chatClient, err := azopenai.NewClientWithKeyCredential(azureOpenAI.Endpoint, cred, newClientOptionsForTest(t)) require.NoError(t, err) - testGetChatCompletions(t, chatClient, true) + testGetChatCompletions(t, chatClient, azureOpenAI) } func TestClient_GetChatCompletionsStream(t *testing.T) { - chatClient := newAzureOpenAIClientForTest(t, canaryChatCompletionsModelDeployment, true) - testGetChatCompletionsStream(t, chatClient) + chatClient := newAzureOpenAIClientForTest(t, azureOpenAICanary) + testGetChatCompletionsStream(t, chatClient, azureOpenAICanary) } func TestClient_OpenAI_GetChatCompletions(t *testing.T) { @@ -58,7 +60,7 @@ func TestClient_OpenAI_GetChatCompletions(t *testing.T) { } chatClient := newOpenAIClientForTest(t) - testGetChatCompletions(t, chatClient, false) + testGetChatCompletions(t, chatClient, openAI) } func TestClient_OpenAI_GetChatCompletionsStream(t *testing.T) { @@ -67,10 +69,10 @@ func TestClient_OpenAI_GetChatCompletionsStream(t *testing.T) { } chatClient := newOpenAIClientForTest(t) - testGetChatCompletionsStream(t, chatClient) + testGetChatCompletionsStream(t, chatClient, openAI) } -func testGetChatCompletions(t *testing.T, client *azopenai.Client, isAzure bool) { +func testGetChatCompletions(t *testing.T, client *azopenai.Client, tv testVars) { expected := azopenai.ChatCompletions{ Choices: []azopenai.ChatChoice{ { @@ -91,10 +93,10 @@ func testGetChatCompletions(t *testing.T, client *azopenai.Client, isAzure bool) }, } - resp, err := client.GetChatCompletions(context.Background(), chatCompletionsRequest, nil) + resp, err := client.GetChatCompletions(context.Background(), newTestChatCompletionOptions(tv), nil) require.NoError(t, err) - if isAzure { + if tv.Azure { // Azure also provides content-filtering. This particular prompt and responses // will be considered safe. expected.PromptAnnotations = []azopenai.PromptFilterResult{ @@ -112,8 +114,8 @@ func testGetChatCompletions(t *testing.T, client *azopenai.Client, isAzure bool) require.Equal(t, expected, resp.ChatCompletions) } -func testGetChatCompletionsStream(t *testing.T, client *azopenai.Client) { - streamResp, err := client.GetChatCompletionsStream(context.Background(), chatCompletionsRequest, nil) +func testGetChatCompletionsStream(t *testing.T, client *azopenai.Client, tv testVars) { + streamResp, err := client.GetChatCompletionsStream(context.Background(), newTestChatCompletionOptions(tv), nil) require.NoError(t, err) // the data comes back differently for streaming @@ -178,19 +180,19 @@ func TestClient_GetChatCompletions_DefaultAzureCredential(t *testing.T) { }) require.NoError(t, err) - chatClient, err := azopenai.NewClient(endpoint, dac, chatCompletionsModelDeployment, &azopenai.ClientOptions{ + chatClient, err := azopenai.NewClient(azureOpenAI.Endpoint, dac, &azopenai.ClientOptions{ ClientOptions: policy.ClientOptions{Transport: recordingTransporter}, }) require.NoError(t, err) - testGetChatCompletions(t, chatClient, true) + testGetChatCompletions(t, chatClient, azureOpenAI) } func TestClient_GetChatCompletions_InvalidModel(t *testing.T) { - cred, err := azopenai.NewKeyCredential(apiKey) + cred, err := azopenai.NewKeyCredential(azureOpenAI.APIKey) require.NoError(t, err) - chatClient, err := azopenai.NewClientWithKeyCredential(endpoint, cred, "thisdoesntexist", newClientOptionsForTest(t)) + chatClient, err := azopenai.NewClientWithKeyCredential(azureOpenAI.Endpoint, cred, newClientOptionsForTest(t)) require.NoError(t, err) _, err = chatClient.GetChatCompletions(context.Background(), azopenai.ChatCompletionsOptions{ @@ -200,8 +202,9 @@ func TestClient_GetChatCompletions_InvalidModel(t *testing.T) { Content: to.Ptr("Count to 100, with a comma between each number and no newlines. E.g., 1, 2, 3, ..."), }, }, - MaxTokens: to.Ptr(int32(1024)), - Temperature: to.Ptr(float32(0.0)), + MaxTokens: to.Ptr(int32(1024)), + Temperature: to.Ptr(float32(0.0)), + DeploymentID: "invalid model name", }, nil) var respErr *azcore.ResponseError @@ -214,20 +217,17 @@ func TestClient_GetChatCompletionsStream_Error(t *testing.T) { t.Skip() } - doTest := func(t *testing.T, client *azopenai.Client) { - t.Helper() - streamResp, err := client.GetChatCompletionsStream(context.Background(), chatCompletionsRequest, nil) + t.Run("AzureOpenAI", func(t *testing.T) { + client := newBogusAzureOpenAIClient(t) + streamResp, err := client.GetChatCompletionsStream(context.Background(), newTestChatCompletionOptions(azureOpenAI), nil) require.Empty(t, streamResp) assertResponseIsError(t, err) - } - - t.Run("AzureOpenAI", func(t *testing.T) { - client := newBogusAzureOpenAIClient(t, chatCompletionsModelDeployment) - doTest(t, client) }) t.Run("OpenAI", func(t *testing.T) { client := newBogusOpenAIClient(t) - doTest(t, client) + streamResp, err := client.GetChatCompletionsStream(context.Background(), newTestChatCompletionOptions(openAI), nil) + require.Empty(t, streamResp) + assertResponseIsError(t, err) }) } diff --git a/sdk/cognitiveservices/azopenai/client_completions_test.go b/sdk/cognitiveservices/azopenai/client_completions_test.go index 766791501c4f..b167a9eb10b3 100644 --- a/sdk/cognitiveservices/azopenai/client_completions_test.go +++ b/sdk/cognitiveservices/azopenai/client_completions_test.go @@ -16,13 +16,13 @@ import ( ) func TestClient_GetCompletions_AzureOpenAI(t *testing.T) { - cred, err := azopenai.NewKeyCredential(apiKey) + cred, err := azopenai.NewKeyCredential(azureOpenAI.APIKey) require.NoError(t, err) - client, err := azopenai.NewClientWithKeyCredential(endpoint, cred, completionsModelDeployment, newClientOptionsForTest(t)) + client, err := azopenai.NewClientWithKeyCredential(azureOpenAI.Endpoint, cred, newClientOptionsForTest(t)) require.NoError(t, err) - testGetCompletions(t, client) + testGetCompletions(t, client, true) } func TestClient_GetCompletions_OpenAI(t *testing.T) { @@ -31,15 +31,21 @@ func TestClient_GetCompletions_OpenAI(t *testing.T) { } client := newOpenAIClientForTest(t) - testGetCompletions(t, client) + testGetCompletions(t, client, false) } -func testGetCompletions(t *testing.T, client *azopenai.Client) { +func testGetCompletions(t *testing.T, client *azopenai.Client, isAzure bool) { + deploymentID := openAI.Completions + + if isAzure { + deploymentID = azureOpenAI.Completions + } + resp, err := client.GetCompletions(context.Background(), azopenai.CompletionsOptions{ - Prompt: []string{"What is Azure OpenAI?"}, - MaxTokens: to.Ptr(int32(2048 - 127)), - Temperature: to.Ptr(float32(0.0)), - Model: &openAICompletionsModel, + Prompt: []string{"What is Azure OpenAI?"}, + MaxTokens: to.Ptr(int32(2048 - 127)), + Temperature: to.Ptr(float32(0.0)), + DeploymentID: deploymentID, }, nil) require.NoError(t, err) diff --git a/sdk/cognitiveservices/azopenai/client_embeddings_test.go b/sdk/cognitiveservices/azopenai/client_embeddings_test.go index 8d61d0c0067d..fc0195e2a401 100644 --- a/sdk/cognitiveservices/azopenai/client_embeddings_test.go +++ b/sdk/cognitiveservices/azopenai/client_embeddings_test.go @@ -13,13 +13,15 @@ import ( ) func TestClient_GetEmbeddings_InvalidModel(t *testing.T) { - cred, err := azopenai.NewKeyCredential(apiKey) + cred, err := azopenai.NewKeyCredential(azureOpenAI.APIKey) require.NoError(t, err) - chatClient, err := azopenai.NewClientWithKeyCredential(endpoint, cred, "thisdoesntexist", newClientOptionsForTest(t)) + chatClient, err := azopenai.NewClientWithKeyCredential(azureOpenAI.Endpoint, cred, newClientOptionsForTest(t)) require.NoError(t, err) - _, err = chatClient.GetEmbeddings(context.Background(), azopenai.EmbeddingsOptions{}, nil) + _, err = chatClient.GetEmbeddings(context.Background(), azopenai.EmbeddingsOptions{ + DeploymentID: "thisdoesntexist", + }, nil) var respErr *azcore.ResponseError require.ErrorAs(t, err, &respErr) @@ -32,21 +34,17 @@ func TestClient_OpenAI_GetEmbeddings(t *testing.T) { } client := newOpenAIClientForTest(t) - modelID := "text-similarity-curie-001" - testGetEmbeddings(t, client, modelID) + testGetEmbeddings(t, client, openAI.Embeddings) } func TestClient_GetEmbeddings(t *testing.T) { - // model deployment points to `text-similarity-curie-001` - deploymentID := "embedding" - - cred, err := azopenai.NewKeyCredential(apiKey) + cred, err := azopenai.NewKeyCredential(azureOpenAI.APIKey) require.NoError(t, err) - client, err := azopenai.NewClientWithKeyCredential(endpoint, cred, deploymentID, newClientOptionsForTest(t)) + client, err := azopenai.NewClientWithKeyCredential(azureOpenAI.Endpoint, cred, newClientOptionsForTest(t)) require.NoError(t, err) - testGetEmbeddings(t, client, deploymentID) + testGetEmbeddings(t, client, azureOpenAI.Embeddings) } func testGetEmbeddings(t *testing.T, client *azopenai.Client, modelOrDeploymentID string) { @@ -71,8 +69,8 @@ func testGetEmbeddings(t *testing.T, client *azopenai.Client, modelOrDeploymentI ctx: context.TODO(), deploymentID: modelOrDeploymentID, body: azopenai.EmbeddingsOptions{ - Input: []string{"\"Your text string goes here\""}, - Model: &modelOrDeploymentID, + Input: []string{"\"Your text string goes here\""}, + DeploymentID: modelOrDeploymentID, }, options: nil, }, diff --git a/sdk/cognitiveservices/azopenai/client_functions_test.go b/sdk/cognitiveservices/azopenai/client_functions_test.go index 92bf46f52acd..60ef50cbc583 100644 --- a/sdk/cognitiveservices/azopenai/client_functions_test.go +++ b/sdk/cognitiveservices/azopenai/client_functions_test.go @@ -30,18 +30,18 @@ func TestGetChatCompletions_usingFunctions(t *testing.T) { t.Run("OpenAI", func(t *testing.T) { chatClient := newOpenAIClientForTest(t) - testChatCompletionsFunctions(t, chatClient) + testChatCompletionsFunctions(t, chatClient, openAI) }) t.Run("AzureOpenAI", func(t *testing.T) { - chatClient := newAzureOpenAIClientForTest(t, chatCompletionsModelDeployment, false) - testChatCompletionsFunctions(t, chatClient) + chatClient := newAzureOpenAIClientForTest(t, azureOpenAI) + testChatCompletionsFunctions(t, chatClient, azureOpenAI) }) } -func testChatCompletionsFunctions(t *testing.T, chatClient *azopenai.Client) { - resp, err := chatClient.GetChatCompletions(context.Background(), azopenai.ChatCompletionsOptions{ - Model: to.Ptr("gpt-4-0613"), +func testChatCompletionsFunctions(t *testing.T, chatClient *azopenai.Client, tv testVars) { + body := azopenai.ChatCompletionsOptions{ + DeploymentID: tv.ChatCompletions, Messages: []azopenai.ChatMessage{ { Role: to.Ptr(azopenai.ChatRoleUser), @@ -72,7 +72,9 @@ func testChatCompletionsFunctions(t *testing.T, chatClient *azopenai.Client) { }, }, Temperature: to.Ptr[float32](0.0), - }, nil) + } + + resp, err := chatClient.GetChatCompletions(context.Background(), body, nil) require.NoError(t, err) funcCall := resp.ChatCompletions.Choices[0].Message.FunctionCall diff --git a/sdk/cognitiveservices/azopenai/client_rai_test.go b/sdk/cognitiveservices/azopenai/client_rai_test.go index 69de5c96c4af..918006d3c6d8 100644 --- a/sdk/cognitiveservices/azopenai/client_rai_test.go +++ b/sdk/cognitiveservices/azopenai/client_rai_test.go @@ -19,13 +19,13 @@ import ( func TestClient_GetCompletions_AzureOpenAI_ContentFilter_Response(t *testing.T) { // Scenario: Your API call asks for multiple responses (N>1) and at least 1 of the responses is filtered // https://github.com/MicrosoftDocs/azure-docs/blob/main/articles/cognitive-services/openai/concepts/content-filter.md#scenario-your-api-call-asks-for-multiple-responses-n1-and-at-least-1-of-the-responses-is-filtered - client := newAzureOpenAIClientForTest(t, completionsModelDeployment, false) + client := newAzureOpenAIClientForTest(t, azureOpenAI) resp, err := client.GetCompletions(context.Background(), azopenai.CompletionsOptions{ - Prompt: []string{"How do I rob a bank?"}, - MaxTokens: to.Ptr(int32(2048 - 127)), - Temperature: to.Ptr(float32(0.0)), - Model: &openAICompletionsModel, + Prompt: []string{"How do I rob a bank?"}, + MaxTokens: to.Ptr(int32(2048 - 127)), + Temperature: to.Ptr(float32(0.0)), + DeploymentID: azureOpenAI.Completions, }, nil) require.Empty(t, resp) @@ -33,31 +33,31 @@ func TestClient_GetCompletions_AzureOpenAI_ContentFilter_Response(t *testing.T) } func TestClient_GetChatCompletions_AzureOpenAI_ContentFilterWithError(t *testing.T) { - client := newAzureOpenAIClientForTest(t, canaryChatCompletionsModelDeployment, true) + client := newAzureOpenAIClientForTest(t, azureOpenAICanary) resp, err := client.GetChatCompletions(context.Background(), azopenai.ChatCompletionsOptions{ Messages: []azopenai.ChatMessage{ {Role: to.Ptr(azopenai.ChatRoleSystem), Content: to.Ptr("You are a helpful assistant.")}, {Role: to.Ptr(azopenai.ChatRoleUser), Content: to.Ptr("How do I rob a bank?")}, }, - MaxTokens: to.Ptr(int32(2048 - 127)), - Temperature: to.Ptr(float32(0.0)), - Model: &openAIChatCompletionsModel, + MaxTokens: to.Ptr(int32(2048 - 127)), + Temperature: to.Ptr(float32(0.0)), + DeploymentID: azureOpenAICanary.ChatCompletions, }, nil) require.Empty(t, resp) assertContentFilterError(t, err, true) } func TestClient_GetChatCompletions_AzureOpenAI_ContentFilter_WithResponse(t *testing.T) { - client := newAzureOpenAIClientForTest(t, canaryChatCompletionsModelDeployment, true) + client := newAzureOpenAIClientForTest(t, azureOpenAICanary) resp, err := client.GetChatCompletions(context.Background(), azopenai.ChatCompletionsOptions{ Messages: []azopenai.ChatMessage{ {Role: to.Ptr(azopenai.ChatRoleUser), Content: to.Ptr("How do I cook a bell pepper?")}, }, - MaxTokens: to.Ptr(int32(2048 - 127)), - Temperature: to.Ptr(float32(0.0)), - Model: &openAIChatCompletionsModel, + MaxTokens: to.Ptr(int32(2048 - 127)), + Temperature: to.Ptr(float32(0.0)), + DeploymentID: azureOpenAICanary.ChatCompletions, }, nil) require.NoError(t, err) diff --git a/sdk/cognitiveservices/azopenai/client_shared_test.go b/sdk/cognitiveservices/azopenai/client_shared_test.go index 94e201ba8dd6..1fadc85582f0 100644 --- a/sdk/cognitiveservices/azopenai/client_shared_test.go +++ b/sdk/cognitiveservices/azopenai/client_shared_test.go @@ -22,37 +22,65 @@ import ( ) var ( - endpoint string // env: AOAI_ENDPOINT - apiKey string // env: AOAI_API_KEY - completionsModelDeployment string // env: AOAI_COMPLETIONS_MODEL_DEPLOYMENT - chatCompletionsModelDeployment string // env: AOAI_CHAT_COMPLETIONS_MODEL_DEPLOYMENT - - canaryEndpoint string // env: AOAI_ENDPOINT_CANARY - canaryAPIKey string // env: AOAI_API_KEY_CANARY - canaryCompletionsModelDeployment string // env: AOAI_COMPLETIONS_MODEL_DEPLOYMENT_CANARY - canaryChatCompletionsModelDeployment string // env: AOAI_CHAT_COMPLETIONS_MODEL_DEPLOYMENT_CANARY - - openAIKey string // env: OPENAI_API_KEY - openAIEndpoint string // env: OPENAI_ENDPOINT - openAICompletionsModel string // env: OPENAI_CHAT_COMPLETIONS_MODEL - openAIChatCompletionsModel string // env: OPENAI_COMPLETIONS_MODEL + azureOpenAI testVars + azureOpenAICanary testVars + openAI testVars ) -func getVars(suffix string) (endpoint, apiKey, completionsModelDeployment, chatCompletionsModelDeployment string) { - endpoint = os.Getenv("AOAI_ENDPOINT" + suffix) +type testVars struct { + Endpoint string // env: AOAI_ENDPOINT, OPENAI_ENDPOINT + APIKey string // env: AOAI_API_KEY, OPENAI_API_KEY + Completions string // env: AOAI_COMPLETIONS_MODEL_DEPLOYMENT, OPENAI_COMPLETIONS_MODEL + ChatCompletions string // env: AOAI_CHAT_COMPLETIONS_MODEL_DEPLOYMENT, OPENAI_CHAT_COMPLETIONS_MODEL + Embeddings string // env: AOAI_EMBEDDINGS_MODEL_DEPLOYMENT, OPENAI_EMBEDDINGS_MODEL + Azure bool +} - if endpoint != "" && !strings.HasSuffix(endpoint, "/") { - // (this just makes recording replacement easier) - endpoint += "/" +func newTestVars(prefix string, isCanary bool) testVars { + getRequired := func(name string) string { + v := os.Getenv(name) + + if v == "" { + panic(fmt.Sprintf("Env variable %s is missing", name)) + } + + return v + } + + azure := prefix == "AOAI" + + canarySuffix := "" + deplSuffix := "" + + if azure { + deplSuffix += "_DEPLOYMENT" + } + + if isCanary { + canarySuffix += "_CANARY" } - apiKey = os.Getenv("AOAI_API_KEY" + suffix) - completionsModelDeployment = os.Getenv("AOAI_COMPLETIONS_MODEL_DEPLOYMENT" + suffix) + tv := testVars{ + Endpoint: getRequired(prefix + "_ENDPOINT" + canarySuffix), + APIKey: getRequired(prefix + "_API_KEY" + canarySuffix), - // ex: gpt-4-0613 - chatCompletionsModelDeployment = os.Getenv("AOAI_CHAT_COMPLETIONS_MODEL_DEPLOYMENT" + suffix) + Completions: getRequired(prefix + "_COMPLETIONS_MODEL" + deplSuffix + canarySuffix), - return + // ex: gpt-4-0613 + ChatCompletions: getRequired(prefix + "_CHAT_COMPLETIONS_MODEL" + deplSuffix + canarySuffix), + + // ex: embedding + Embeddings: getRequired(prefix + "_EMBEDDINGS_MODEL" + deplSuffix + canarySuffix), + + Azure: azure, + } + + if tv.Endpoint != "" && !strings.HasSuffix(tv.Endpoint, "/") { + // (this just makes recording replacement easier) + tv.Endpoint += "/" + } + + return tv } const fakeEndpoint = "https://recordedhost/" @@ -60,39 +88,35 @@ const fakeAPIKey = "redacted" func initEnvVars() { if recording.GetRecordMode() == recording.PlaybackMode { - endpoint = fakeEndpoint - apiKey = fakeAPIKey - openAIKey = fakeAPIKey - openAIEndpoint = fakeEndpoint - - canaryEndpoint = fakeEndpoint - canaryAPIKey = fakeAPIKey - canaryCompletionsModelDeployment = "" - canaryChatCompletionsModelDeployment = "gpt-4" - - completionsModelDeployment = "text-davinci-003" - openAICompletionsModel = "text-davinci-003" - - chatCompletionsModelDeployment = "gpt-4-0613" - openAIChatCompletionsModel = "gpt-4" + azureOpenAI.Azure = true + azureOpenAI.Endpoint = fakeEndpoint + azureOpenAI.APIKey = fakeAPIKey + openAI.APIKey = fakeAPIKey + openAI.Endpoint = fakeEndpoint + + azureOpenAICanary.Azure = true + azureOpenAICanary.Endpoint = fakeEndpoint + azureOpenAICanary.APIKey = fakeAPIKey + azureOpenAICanary.Completions = "" + azureOpenAICanary.ChatCompletions = "gpt-4" + + azureOpenAI.Completions = "text-davinci-003" + openAI.Completions = "text-davinci-003" + + azureOpenAI.ChatCompletions = "gpt-4-0613" + openAI.ChatCompletions = "gpt-4-0613" + + openAI.Embeddings = "text-similarity-curie-001" + azureOpenAI.Embeddings = "embedding" } else { if err := godotenv.Load(); err != nil { fmt.Printf("Failed to load .env file: %s\n", err) os.Exit(1) } - endpoint, apiKey, completionsModelDeployment, chatCompletionsModelDeployment = getVars("") - canaryEndpoint, canaryAPIKey, canaryCompletionsModelDeployment, canaryChatCompletionsModelDeployment = getVars("_CANARY") - - openAIKey = os.Getenv("OPENAI_API_KEY") - openAIEndpoint = os.Getenv("OPENAI_ENDPOINT") - openAICompletionsModel = os.Getenv("OPENAI_COMPLETIONS_MODEL") - openAIChatCompletionsModel = os.Getenv("OPENAI_CHAT_COMPLETIONS_MODEL") // ex: gpt-4-0613 - - if openAIEndpoint != "" && !strings.HasSuffix(openAIEndpoint, "/") { - // (this just makes recording replacement easier) - openAIEndpoint += "/" - } + azureOpenAI = newTestVars("AOAI", false) + azureOpenAICanary = newTestVars("AOAI", true) + openAI = newTestVars("OPENAI", false) } } @@ -111,17 +135,17 @@ func newRecordingTransporter(t *testing.T) policy.Transporter { require.NoError(t, err) // "RequestUri": "https://openai-shared.openai.azure.com/openai/deployments/text-davinci-003/completions?api-version=2023-03-15-preview", - err = recording.AddURISanitizer(fakeEndpoint, regexp.QuoteMeta(endpoint), nil) + err = recording.AddURISanitizer(fakeEndpoint, regexp.QuoteMeta(azureOpenAI.Endpoint), nil) require.NoError(t, err) - err = recording.AddURISanitizer(fakeEndpoint, regexp.QuoteMeta(canaryEndpoint), nil) + err = recording.AddURISanitizer(fakeEndpoint, regexp.QuoteMeta(azureOpenAICanary.Endpoint), nil) require.NoError(t, err) err = recording.AddURISanitizer("/openai/operations/images/00000000-AAAA-BBBB-CCCC-DDDDDDDDDDDD", "/openai/operations/images/[A-Za-z-0-9]+", nil) require.NoError(t, err) - if openAIEndpoint != "" { - err = recording.AddURISanitizer(fakeEndpoint, regexp.QuoteMeta(openAIEndpoint), nil) + if openAI.Endpoint != "" { + err = recording.AddURISanitizer(fakeEndpoint, regexp.QuoteMeta(openAI.Endpoint), nil) require.NoError(t, err) } } @@ -166,30 +190,22 @@ func newClientOptionsForTest(t *testing.T) *azopenai.ClientOptions { // newAzureOpenAIClientForTest can create a client pointing to the "canary" endpoint (basically - leading fixes or features) // or the current deployed endpoint. -func newAzureOpenAIClientForTest(t *testing.T, modelDeploymentID string, useCanary bool) *azopenai.Client { - var apiKey = apiKey - var endpoint = endpoint - - if useCanary { - apiKey = canaryAPIKey - endpoint = canaryEndpoint - } - - cred, err := azopenai.NewKeyCredential(apiKey) +func newAzureOpenAIClientForTest(t *testing.T, tv testVars) *azopenai.Client { + cred, err := azopenai.NewKeyCredential(tv.APIKey) require.NoError(t, err) - client, err := azopenai.NewClientWithKeyCredential(endpoint, cred, modelDeploymentID, newClientOptionsForTest(t)) + client, err := azopenai.NewClientWithKeyCredential(tv.Endpoint, cred, newClientOptionsForTest(t)) require.NoError(t, err) return client } func newOpenAIClientForTest(t *testing.T) *azopenai.Client { - if openAIKey == "" { + if openAI.APIKey == "" { t.Skipf("OPENAI_API_KEY not defined, skipping OpenAI public endpoint test") } - cred, err := azopenai.NewKeyCredential(openAIKey) + cred, err := azopenai.NewKeyCredential(openAI.APIKey) require.NoError(t, err) // we get rate limited quite a bit. @@ -205,7 +221,7 @@ func newOpenAIClientForTest(t *testing.T) *azopenai.Client { MaxRetryDelay: time.Second, } - chatClient, err := azopenai.NewClientForOpenAI(openAIEndpoint, cred, options) + chatClient, err := azopenai.NewClientForOpenAI(openAI.Endpoint, cred, options) require.NoError(t, err) return chatClient @@ -213,11 +229,11 @@ func newOpenAIClientForTest(t *testing.T) *azopenai.Client { // newBogusAzureOpenAIClient creates a client that uses an invalid key, which will cause Azure OpenAI to return // a failure. -func newBogusAzureOpenAIClient(t *testing.T, modelDeploymentID string) *azopenai.Client { +func newBogusAzureOpenAIClient(t *testing.T) *azopenai.Client { cred, err := azopenai.NewKeyCredential("bogus-api-key") require.NoError(t, err) - client, err := azopenai.NewClientWithKeyCredential(endpoint, cred, modelDeploymentID, newClientOptionsForTest(t)) + client, err := azopenai.NewClientWithKeyCredential(azureOpenAI.Endpoint, cred, newClientOptionsForTest(t)) require.NoError(t, err) return client } @@ -228,7 +244,7 @@ func newBogusOpenAIClient(t *testing.T) *azopenai.Client { cred, err := azopenai.NewKeyCredential("bogus-api-key") require.NoError(t, err) - client, err := azopenai.NewClientForOpenAI(openAIEndpoint, cred, newClientOptionsForTest(t)) + client, err := azopenai.NewClientForOpenAI(openAI.Endpoint, cred, newClientOptionsForTest(t)) require.NoError(t, err) return client } diff --git a/sdk/cognitiveservices/azopenai/client_test.go b/sdk/cognitiveservices/azopenai/client_test.go index 0186ec003003..8a06352fb185 100644 --- a/sdk/cognitiveservices/azopenai/client_test.go +++ b/sdk/cognitiveservices/azopenai/client_test.go @@ -32,7 +32,7 @@ func TestClient_OpenAI_InvalidModel(t *testing.T) { Content: to.Ptr("hello"), }, }, - Model: to.Ptr("non-existent-model"), + DeploymentID: "non-existent-model", }, nil) var respErr *azcore.ResponseError diff --git a/sdk/cognitiveservices/azopenai/custom_client.go b/sdk/cognitiveservices/azopenai/custom_client.go index c4f1da63b198..701fba27797a 100644 --- a/sdk/cognitiveservices/azopenai/custom_client.go +++ b/sdk/cognitiveservices/azopenai/custom_client.go @@ -38,26 +38,24 @@ type ClientOptions struct { // NewClient creates a new instance of Client that connects to an Azure OpenAI endpoint. // - endpoint - Azure OpenAI service endpoint, for example: https://{your-resource-name}.openai.azure.com // - credential - used to authorize requests. Usually a credential from [github.com/Azure/azure-sdk-for-go/sdk/azidentity]. -// - deploymentID - the deployment ID of the model to query // - options - client options, pass nil to accept the default values. -func NewClient(endpoint string, credential azcore.TokenCredential, deploymentID string, options *ClientOptions) (*Client, error) { +func NewClient(endpoint string, credential azcore.TokenCredential, options *ClientOptions) (*Client, error) { if options == nil { options = &ClientOptions{} } authPolicy := runtime.NewBearerTokenPolicy(credential, []string{tokenScope}, nil) azcoreClient, err := azcore.NewClient(clientName, version, runtime.PipelineOptions{PerRetry: []policy.Policy{authPolicy}}, &options.ClientOptions) + if err != nil { return nil, err } - fullEndpoint := formatAzureOpenAIURL(endpoint, deploymentID) return &Client{ internal: azcoreClient, clientData: clientData{ - baseEndpoint: endpoint, - endpoint: fullEndpoint, - azure: true, + endpoint: endpoint, + azure: true, }, }, nil } @@ -65,9 +63,8 @@ func NewClient(endpoint string, credential azcore.TokenCredential, deploymentID // NewClientWithKeyCredential creates a new instance of Client that connects to an Azure OpenAI endpoint. // - endpoint - Azure OpenAI service endpoint, for example: https://{your-resource-name}.openai.azure.com // - credential - used to authorize requests with an API Key credential -// - deploymentID - the deployment ID of the model to query // - options - client options, pass nil to accept the default values. -func NewClientWithKeyCredential(endpoint string, credential KeyCredential, deploymentID string, options *ClientOptions) (*Client, error) { +func NewClientWithKeyCredential(endpoint string, credential KeyCredential, options *ClientOptions) (*Client, error) { if options == nil { options = &ClientOptions{} } @@ -78,13 +75,11 @@ func NewClientWithKeyCredential(endpoint string, credential KeyCredential, deplo return nil, err } - fullEndpoint := formatAzureOpenAIURL(endpoint, deploymentID) return &Client{ internal: azcoreClient, clientData: clientData{ - baseEndpoint: endpoint, - endpoint: fullEndpoint, - azure: true, + endpoint: endpoint, + azure: true, }, }, nil } @@ -106,9 +101,8 @@ func NewClientForOpenAI(endpoint string, credential KeyCredential, options *Clie return &Client{ internal: azcoreClient, clientData: clientData{ - baseEndpoint: endpoint, - endpoint: endpoint, - azure: false, + endpoint: endpoint, + azure: false, }, }, nil } @@ -161,13 +155,16 @@ func (o streamCompletionsOptions) MarshalJSON() ([]byte, error) { // If the operation fails it returns an *azcore.ResponseError type. // - options - GetCompletionsOptions contains the optional parameters for the Client.GetCompletions method. func (client *Client) GetCompletionsStream(ctx context.Context, body CompletionsOptions, options *GetCompletionsStreamOptions) (GetCompletionsStreamResponse, error) { - req, err := client.getCompletionsCreateRequest(ctx, CompletionsOptions{}, &GetCompletionsOptions{}) + req, err := client.getCompletionsCreateRequest(ctx, body, &GetCompletionsOptions{}) if err != nil { return GetCompletionsStreamResponse{}, err } - if err := runtime.MarshalAsJSON(req, streamCompletionsOptions{body, true}); err != nil { + if err := runtime.MarshalAsJSON(req, streamCompletionsOptions{ + any: body, + Stream: true, + }); err != nil { return GetCompletionsStreamResponse{}, err } @@ -188,22 +185,20 @@ func (client *Client) GetCompletionsStream(ctx context.Context, body Completions }, nil } -func formatAzureOpenAIURL(endpoint, deploymentID string) string { - escapedDeplID := url.PathEscape(deploymentID) - return runtime.JoinPaths(endpoint, "openai", "deployments", escapedDeplID) -} - // GetChatCompletionsStream - Return the chat completions for a given prompt as a sequence of events. // 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, ChatCompletionsOptions{}, &GetChatCompletionsOptions{}) + req, err := client.getChatCompletionsCreateRequest(ctx, body, &GetChatCompletionsOptions{}) if err != nil { return GetChatCompletionsStreamResponse{}, err } - if err := runtime.MarshalAsJSON(req, streamCompletionsOptions{body, true}); err != nil { + if err := runtime.MarshalAsJSON(req, streamCompletionsOptions{ + any: body, + Stream: true, + }); err != nil { return GetChatCompletionsStreamResponse{}, err } @@ -224,12 +219,17 @@ func (client *Client) GetChatCompletionsStream(ctx context.Context, body ChatCom }, nil } -func (client *Client) formatURL(path string) string { +func (client *Client) formatURL(path string, deploymentID 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.baseEndpoint, "openai", path) + return runtime.JoinPaths(client.endpoint, "openai", path) default: + if client.azure { + escapedDeplID := url.PathEscape(deploymentID) + return runtime.JoinPaths(client.endpoint, "openai", "deployments", escapedDeplID, path) + } + return runtime.JoinPaths(client.endpoint, path) } } @@ -239,7 +239,21 @@ func (client *Client) newError(resp *http.Response) error { } type clientData struct { - endpoint string - baseEndpoint string - azure bool + endpoint string + azure bool +} + +func getDeploymentID[T ChatCompletionsOptions | CompletionsOptions | EmbeddingsOptions | ImageGenerationOptions](v T) string { + switch a := any(v).(type) { + case ChatCompletionsOptions: + return a.DeploymentID + case CompletionsOptions: + return a.DeploymentID + case EmbeddingsOptions: + return a.DeploymentID + case ImageGenerationOptions: + return "" + default: + return "" + } } diff --git a/sdk/cognitiveservices/azopenai/custom_client_image.go b/sdk/cognitiveservices/azopenai/custom_client_image.go index e244eb38ea0c..d3a702006753 100644 --- a/sdk/cognitiveservices/azopenai/custom_client_image.go +++ b/sdk/cognitiveservices/azopenai/custom_client_image.go @@ -54,7 +54,7 @@ func generateImageWithAzure(client *Client, ctx context.Context, body ImageGener func generateImageWithOpenAI(ctx context.Context, client *Client, body ImageGenerationOptions) (CreateImageResponse, error) { urlPath := "/images/generations" - req, err := runtime.NewRequest(ctx, http.MethodPost, client.formatURL(urlPath)) + req, err := runtime.NewRequest(ctx, http.MethodPost, client.formatURL(urlPath, "")) if err != nil { return CreateImageResponse{}, err } diff --git a/sdk/cognitiveservices/azopenai/custom_client_image_test.go b/sdk/cognitiveservices/azopenai/custom_client_image_test.go index b7bd5a39f8c8..5e6643e0c453 100644 --- a/sdk/cognitiveservices/azopenai/custom_client_image_test.go +++ b/sdk/cognitiveservices/azopenai/custom_client_image_test.go @@ -26,10 +26,10 @@ func TestImageGeneration_AzureOpenAI(t *testing.T) { t.Skipf("Ignoring poller-based test") } - cred, err := azopenai.NewKeyCredential(apiKey) + cred, err := azopenai.NewKeyCredential(azureOpenAI.APIKey) require.NoError(t, err) - client, err := azopenai.NewClientWithKeyCredential(endpoint, cred, "", newClientOptionsForTest(t)) + client, err := azopenai.NewClientWithKeyCredential(azureOpenAI.Endpoint, cred, newClientOptionsForTest(t)) require.NoError(t, err) testImageGeneration(t, client, azopenai.ImageGenerationResponseFormatURL) @@ -49,7 +49,7 @@ func TestImageGeneration_AzureOpenAI_WithError(t *testing.T) { t.Skip() } - client := newBogusAzureOpenAIClient(t, "") + client := newBogusAzureOpenAIClient(t) testImageGenerationFailure(t, client) } diff --git a/sdk/cognitiveservices/azopenai/custom_client_test.go b/sdk/cognitiveservices/azopenai/custom_client_test.go index a45aeffd15af..22b07e0e6b73 100644 --- a/sdk/cognitiveservices/azopenai/custom_client_test.go +++ b/sdk/cognitiveservices/azopenai/custom_client_test.go @@ -22,10 +22,9 @@ import ( func TestNewClient(t *testing.T) { type args struct { - endpoint string - credential azcore.TokenCredential - deploymentID string - options *azopenai.ClientOptions + endpoint string + credential azcore.TokenCredential + options *azopenai.ClientOptions } tests := []struct { name string @@ -37,7 +36,7 @@ func TestNewClient(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := azopenai.NewClient(tt.args.endpoint, tt.args.credential, tt.args.deploymentID, tt.args.options) + 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 @@ -51,10 +50,9 @@ func TestNewClient(t *testing.T) { func TestNewClientWithKeyCredential(t *testing.T) { type args struct { - endpoint string - credential azopenai.KeyCredential - deploymentID string - options *azopenai.ClientOptions + endpoint string + credential azopenai.KeyCredential + options *azopenai.ClientOptions } tests := []struct { name string @@ -66,7 +64,7 @@ func TestNewClientWithKeyCredential(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := azopenai.NewClientWithKeyCredential(tt.args.endpoint, tt.args.credential, tt.args.deploymentID, tt.args.options) + 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 @@ -79,13 +77,13 @@ func TestNewClientWithKeyCredential(t *testing.T) { } func TestGetCompletionsStream_AzureOpenAI(t *testing.T) { - cred, err := azopenai.NewKeyCredential(apiKey) + cred, err := azopenai.NewKeyCredential(azureOpenAI.APIKey) require.NoError(t, err) - client, err := azopenai.NewClientWithKeyCredential(endpoint, cred, completionsModelDeployment, newClientOptionsForTest(t)) + client, err := azopenai.NewClientWithKeyCredential(azureOpenAI.Endpoint, cred, newClientOptionsForTest(t)) require.NoError(t, err) - testGetCompletionsStream(t, client) + testGetCompletionsStream(t, client, azureOpenAI) } func TestGetCompletionsStream_OpenAI(t *testing.T) { @@ -94,15 +92,15 @@ func TestGetCompletionsStream_OpenAI(t *testing.T) { } client := newOpenAIClientForTest(t) - testGetCompletionsStream(t, client) + testGetCompletionsStream(t, client, openAI) } -func testGetCompletionsStream(t *testing.T, client *azopenai.Client) { +func testGetCompletionsStream(t *testing.T, client *azopenai.Client, tv testVars) { body := azopenai.CompletionsOptions{ - Prompt: []string{"What is Azure OpenAI?"}, - MaxTokens: to.Ptr(int32(2048)), - Temperature: to.Ptr(float32(0.0)), - Model: to.Ptr(openAICompletionsModel), + Prompt: []string{"What is Azure OpenAI?"}, + MaxTokens: to.Ptr(int32(2048)), + Temperature: to.Ptr(float32(0.0)), + DeploymentID: tv.Completions, } response, err := client.GetCompletionsStream(context.TODO(), body, nil) @@ -154,24 +152,24 @@ func TestClient_GetCompletions_Error(t *testing.T) { t.Skip() } - doTest := func(t *testing.T, client *azopenai.Client) { + doTest := func(t *testing.T, client *azopenai.Client, model string) { streamResp, err := client.GetCompletionsStream(context.Background(), azopenai.CompletionsOptions{ - Prompt: []string{"What is Azure OpenAI?"}, - MaxTokens: to.Ptr(int32(2048 - 127)), - Temperature: to.Ptr(float32(0.0)), - Model: &openAICompletionsModel, + Prompt: []string{"What is Azure OpenAI?"}, + MaxTokens: to.Ptr(int32(2048 - 127)), + Temperature: to.Ptr(float32(0.0)), + DeploymentID: model, }, nil) require.Empty(t, streamResp) assertResponseIsError(t, err) } t.Run("AzureOpenAI", func(t *testing.T) { - client := newBogusAzureOpenAIClient(t, completionsModelDeployment) - doTest(t, client) + client := newBogusAzureOpenAIClient(t) + doTest(t, client, azureOpenAI.Completions) }) t.Run("OpenAI", func(t *testing.T) { client := newBogusOpenAIClient(t) - doTest(t, client) + doTest(t, client, openAI.Completions) }) } diff --git a/sdk/cognitiveservices/azopenai/example_client_createimage_test.go b/sdk/cognitiveservices/azopenai/example_client_createimage_test.go index 42287b861ede..4b075526728c 100644 --- a/sdk/cognitiveservices/azopenai/example_client_createimage_test.go +++ b/sdk/cognitiveservices/azopenai/example_client_createimage_test.go @@ -33,7 +33,7 @@ func ExampleClient_CreateImage() { // TODO: handle error } - client, err := azopenai.NewClientWithKeyCredential(azureOpenAIEndpoint, keyCredential, "", nil) + client, err := azopenai.NewClientWithKeyCredential(azureOpenAIEndpoint, keyCredential, nil) if err != nil { // TODO: handle error diff --git a/sdk/cognitiveservices/azopenai/example_client_embeddings_test.go b/sdk/cognitiveservices/azopenai/example_client_embeddings_test.go index ce77480e676c..cf9c6cc092a6 100644 --- a/sdk/cognitiveservices/azopenai/example_client_embeddings_test.go +++ b/sdk/cognitiveservices/azopenai/example_client_embeddings_test.go @@ -31,15 +31,15 @@ func ExampleClient_GetEmbeddings() { // 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, modelDeploymentID, nil) + client, err := azopenai.NewClientWithKeyCredential(azureOpenAIEndpoint, keyCredential, nil) if err != nil { // TODO: handle error } resp, err := client.GetEmbeddings(context.TODO(), azopenai.EmbeddingsOptions{ - Input: []string{"The food was delicious and the waiter..."}, - Model: &modelDeploymentID, + Input: []string{"The food was delicious and the waiter..."}, + DeploymentID: modelDeploymentID, }, nil) if err != nil { diff --git a/sdk/cognitiveservices/azopenai/example_client_getchatcompletions_test.go b/sdk/cognitiveservices/azopenai/example_client_getchatcompletions_test.go index c531f2609c00..61e83535eae4 100644 --- a/sdk/cognitiveservices/azopenai/example_client_getchatcompletions_test.go +++ b/sdk/cognitiveservices/azopenai/example_client_getchatcompletions_test.go @@ -35,7 +35,7 @@ func ExampleClient_GetChatCompletions() { // 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, modelDeploymentID, nil) + client, err := azopenai.NewClientWithKeyCredential(azureOpenAIEndpoint, keyCredential, nil) if err != nil { // TODO: handle error @@ -64,7 +64,8 @@ func ExampleClient_GetChatCompletions() { resp, err := client.GetChatCompletions(context.TODO(), azopenai.ChatCompletionsOptions{ // This is a conversation in progress. // NOTE: all messages count against token usage for this API. - Messages: messages, + Messages: messages, + DeploymentID: modelDeploymentID, }, nil) if err != nil { @@ -103,14 +104,14 @@ func ExampleClient_GetChatCompletions_functions() { // 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, modelDeploymentID, nil) + client, err := azopenai.NewClientWithKeyCredential(azureOpenAIEndpoint, keyCredential, nil) if err != nil { // TODO: handle error } resp, err := client.GetChatCompletions(context.Background(), azopenai.ChatCompletionsOptions{ - Model: &modelDeploymentID, + DeploymentID: modelDeploymentID, Messages: []azopenai.ChatMessage{ { Role: to.Ptr(azopenai.ChatRoleUser), @@ -192,7 +193,7 @@ func ExampleClient_GetChatCompletionsStream() { // 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, modelDeploymentID, nil) + client, err := azopenai.NewClientWithKeyCredential(azureOpenAIEndpoint, keyCredential, nil) if err != nil { // TODO: handle error @@ -219,8 +220,9 @@ func ExampleClient_GetChatCompletionsStream() { resp, err := client.GetChatCompletionsStream(context.TODO(), azopenai.ChatCompletionsOptions{ // This is a conversation in progress. // NOTE: all messages count against token usage for this API. - Messages: messages, - N: to.Ptr[int32](1), + Messages: messages, + N: to.Ptr[int32](1), + DeploymentID: modelDeploymentID, }, nil) if err != nil { diff --git a/sdk/cognitiveservices/azopenai/example_client_getcompletions_test.go b/sdk/cognitiveservices/azopenai/example_client_getcompletions_test.go index 5339502a029b..37ed30a92f0c 100644 --- a/sdk/cognitiveservices/azopenai/example_client_getcompletions_test.go +++ b/sdk/cognitiveservices/azopenai/example_client_getcompletions_test.go @@ -34,16 +34,17 @@ func ExampleClient_GetCompletions() { // 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, modelDeploymentID, nil) + client, err := azopenai.NewClientWithKeyCredential(azureOpenAIEndpoint, keyCredential, nil) if err != nil { // TODO: handle error } resp, err := client.GetCompletions(context.TODO(), azopenai.CompletionsOptions{ - Prompt: []string{"What is Azure OpenAI, in 20 words or less"}, - MaxTokens: to.Ptr(int32(2048)), - Temperature: to.Ptr(float32(0.0)), + Prompt: []string{"What is Azure OpenAI, in 20 words or less"}, + MaxTokens: to.Ptr(int32(2048)), + Temperature: to.Ptr(float32(0.0)), + DeploymentID: modelDeploymentID, }, nil) if err != nil { @@ -77,16 +78,17 @@ func ExampleClient_GetCompletionsStream() { // 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, modelDeploymentID, nil) + client, err := azopenai.NewClientWithKeyCredential(azureOpenAIEndpoint, keyCredential, nil) if err != nil { // TODO: handle error } resp, err := client.GetCompletionsStream(context.TODO(), azopenai.CompletionsOptions{ - Prompt: []string{"What is Azure OpenAI, in 20 words or less?"}, - MaxTokens: to.Ptr(int32(2048)), - Temperature: to.Ptr(float32(0.0)), + Prompt: []string{"What is Azure OpenAI, in 20 words or less?"}, + MaxTokens: to.Ptr(int32(2048)), + Temperature: to.Ptr(float32(0.0)), + DeploymentID: modelDeploymentID, }, nil) if err != nil { diff --git a/sdk/cognitiveservices/azopenai/example_client_test.go b/sdk/cognitiveservices/azopenai/example_client_test.go index e13930b3f680..681449367b05 100644 --- a/sdk/cognitiveservices/azopenai/example_client_test.go +++ b/sdk/cognitiveservices/azopenai/example_client_test.go @@ -33,11 +33,9 @@ func ExampleNewClient() { // TODO: handle error } - modelDeploymentID := "model deployment ID" - // NOTE: this constructor creates a client that connects to an Azure OpenAI endpoint. // To connect to the public OpenAI endpoint, use azopenai.NewClientForOpenAI - client, err := azopenai.NewClient("https://.openai.azure.com", dac, modelDeploymentID, nil) + client, err := azopenai.NewClient("https://.openai.azure.com", dac, nil) if err != nil { // TODO: handle error @@ -53,13 +51,9 @@ func ExampleNewClientWithKeyCredential() { // TODO: handle error } - // 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 - modelDeploymentID := "model deployment ID" - // NOTE: this constructor creates a client that connects to an Azure OpenAI endpoint. // To connect to the public OpenAI endpoint, use azopenai.NewClientForOpenAI - client, err := azopenai.NewClientWithKeyCredential("https://.openai.azure.com", keyCredential, modelDeploymentID, nil) + client, err := azopenai.NewClientWithKeyCredential("https://.openai.azure.com", keyCredential, nil) if err != nil { // TODO: handle error diff --git a/sdk/cognitiveservices/azopenai/models.go b/sdk/cognitiveservices/azopenai/models.go index 35afc8aad73b..ea672fc50ed6 100644 --- a/sdk/cognitiveservices/azopenai/models.go +++ b/sdk/cognitiveservices/azopenai/models.go @@ -237,9 +237,8 @@ type ChatCompletionsOptions struct { // The maximum number of tokens to generate. MaxTokens *int32 - // The model name to provide as part of this completions request. Not applicable to Azure OpenAI, where deployment information - // should be included in the Azure resource URI that's connected to. - Model *string + // DeploymentID specifies the name of the deployment (for Azure OpenAI) or model (for OpenAI) to use for this request. + DeploymentID string // The number of chat completions choices that should be generated for a chat completions response. Because this setting can // generate many completions, it may quickly consume your token quota. Use @@ -432,9 +431,8 @@ type CompletionsOptions struct { // The maximum number of tokens to generate. MaxTokens *int32 - // The model name to provide as part of this completions request. Not applicable to Azure OpenAI, where deployment information - // should be included in the Azure resource URI that's connected to. - Model *string + // DeploymentID specifies the name of the deployment (for Azure OpenAI) or model (for OpenAI) to use for this request. + DeploymentID string // The number of completions choices that should be generated per provided prompt as part of an overall completions response. // Because this setting can generate many completions, it may quickly consume @@ -581,9 +579,8 @@ type EmbeddingsOptions struct { // inferior results when newlines are present. Input []string - // The model name to provide as part of this embeddings request. Not applicable to Azure OpenAI, where deployment information - // should be included in the Azure resource URI that's connected to. - Model *string + // DeploymentID specifies the name of the deployment (for Azure OpenAI) or model (for OpenAI) to use for this request. + DeploymentID string // An identifier for the caller or end user of the operation. This may be used for tracking or rate-limiting purposes. User *string diff --git a/sdk/cognitiveservices/azopenai/models_serde.go b/sdk/cognitiveservices/azopenai/models_serde.go index 4b591a60fca8..7af3419c5f09 100644 --- a/sdk/cognitiveservices/azopenai/models_serde.go +++ b/sdk/cognitiveservices/azopenai/models_serde.go @@ -481,7 +481,7 @@ func (c ChatCompletionsOptions) MarshalJSON() ([]byte, error) { populate(objectMap, "logit_bias", c.LogitBias) populate(objectMap, "max_tokens", c.MaxTokens) populate(objectMap, "messages", c.Messages) - populate(objectMap, "model", c.Model) + populate(objectMap, "model", &c.DeploymentID) populate(objectMap, "n", c.N) populate(objectMap, "presence_penalty", c.PresencePenalty) populate(objectMap, "stop", c.Stop) @@ -519,7 +519,7 @@ func (c *ChatCompletionsOptions) UnmarshalJSON(data []byte) error { err = unpopulate(val, "Messages", &c.Messages) delete(rawMsg, key) case "model": - err = unpopulate(val, "Model", &c.Model) + err = unpopulate(val, "Model", &c.DeploymentID) delete(rawMsg, key) case "n": err = unpopulate(val, "N", &c.N) @@ -829,7 +829,7 @@ func (c CompletionsOptions) MarshalJSON() ([]byte, error) { populate(objectMap, "logit_bias", c.LogitBias) populate(objectMap, "logprobs", c.LogProbs) populate(objectMap, "max_tokens", c.MaxTokens) - populate(objectMap, "model", c.Model) + populate(objectMap, "model", &c.DeploymentID) populate(objectMap, "n", c.N) populate(objectMap, "presence_penalty", c.PresencePenalty) populate(objectMap, "prompt", c.Prompt) @@ -868,7 +868,7 @@ func (c *CompletionsOptions) UnmarshalJSON(data []byte) error { err = unpopulate(val, "MaxTokens", &c.MaxTokens) delete(rawMsg, key) case "model": - err = unpopulate(val, "Model", &c.Model) + err = unpopulate(val, "Model", &c.DeploymentID) delete(rawMsg, key) case "n": err = unpopulate(val, "N", &c.N) @@ -1190,7 +1190,7 @@ func (e *Embeddings) UnmarshalJSON(data []byte) error { func (e EmbeddingsOptions) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) populate(objectMap, "input", e.Input) - populate(objectMap, "model", e.Model) + populate(objectMap, "model", &e.DeploymentID) populate(objectMap, "user", e.User) return json.Marshal(objectMap) } @@ -1208,7 +1208,7 @@ func (e *EmbeddingsOptions) UnmarshalJSON(data []byte) error { err = unpopulate(val, "Input", &e.Input) delete(rawMsg, key) case "model": - err = unpopulate(val, "Model", &e.Model) + err = unpopulate(val, "Model", &e.DeploymentID) delete(rawMsg, key) case "user": err = unpopulate(val, "User", &e.User) diff --git a/sdk/cognitiveservices/azopenai/sample.env b/sdk/cognitiveservices/azopenai/sample.env index c4f5e9396a5d..0a12cbb0ac5a 100644 --- a/sdk/cognitiveservices/azopenai/sample.env +++ b/sdk/cognitiveservices/azopenai/sample.env @@ -7,6 +7,7 @@ AOAI_API_KEY= AOAI_COMPLETIONS_MODEL_DEPLOYMENT= # ex: gpt-4 AOAI_CHAT_COMPLETIONS_MODEL_DEPLOYMENT= +AOAI_EMBEDDINGS_MODEL_DEPLOYMENT= # public OpenAI OPENAI_ENDPOINT=https://api.openai.com/v1 @@ -15,3 +16,4 @@ OPENAI_API_KEY= OPENAI_COMPLETIONS_MODEL= # ex: gpt-4 OPENAI_CHAT_COMPLETIONS_MODEL= +OPENAI_EMBEDDINGS_MODEL= \ No newline at end of file