From 1e98f82e03529506c9afaa650717b3819633d573 Mon Sep 17 00:00:00 2001 From: Mark Pollack Date: Mon, 11 Mar 2024 17:30:40 -0400 Subject: [PATCH] Change Azure embedding and chat options 'model' property to 'deployment-name' * Azure uses 'deployment-name' when provisioning models and is what needs to be passed in to the client, not the model name. This is a difference with the OpenAI API that doesn't have a deployment-name This change aligns the terminology used with Azure so that there is less confusion when setting configuration property values Fixes #10 --- .../azure/openai/AzureOpenAiChatClient.java | 18 +++++++--------- .../azure/openai/AzureOpenAiChatOptions.java | 21 +++++++++---------- .../openai/AzureOpenAiEmbeddingClient.java | 5 +++-- .../openai/AzureOpenAiEmbeddingOptions.java | 21 ++++++++++--------- .../AzureChatCompletionsOptionsTests.java | 4 ++-- .../openai/AzureEmbeddingsOptionsTests.java | 10 +++++++-- .../azure/openai/AzureOpenAiChatClientIT.java | 2 +- .../AzureOpenAiChatClientFunctionCallIT.java | 8 ++++--- .../pages/api/clients/azure-openai-chat.adoc | 2 +- .../embeddings/azure-openai-embeddings.adoc | 2 +- .../openai/AzureOpenAiChatProperties.java | 4 ++-- .../AzureOpenAiEmbeddingProperties.java | 2 +- ...eOpenAiAutoConfigurationPropertyTests.java | 6 +++--- 13 files changed, 56 insertions(+), 49 deletions(-) diff --git a/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatClient.java b/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatClient.java index 8c12d5c34..1ddc732fc 100644 --- a/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatClient.java +++ b/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatClient.java @@ -75,7 +75,7 @@ public class AzureOpenAiChatClient extends AbstractFunctionCallSupport implements ChatClient, StreamingChatClient { - private static final String DEFAULT_MODEL = "gpt-35-turbo"; + private static final String DEFAULT_DEPLOYMENT_NAME = "gpt-35-turbo"; private static final Float DEFAULT_TEMPERATURE = 0.7f; @@ -93,7 +93,10 @@ public class AzureOpenAiChatClient public AzureOpenAiChatClient(OpenAIClient microsoftOpenAiClient) { this(microsoftOpenAiClient, - AzureOpenAiChatOptions.builder().withModel(DEFAULT_MODEL).withTemperature(DEFAULT_TEMPERATURE).build()); + AzureOpenAiChatOptions.builder() + .withDeploymentName(DEFAULT_DEPLOYMENT_NAME) + .withTemperature(DEFAULT_TEMPERATURE) + .build()); } public AzureOpenAiChatClient(OpenAIClient microsoftOpenAiClient, AzureOpenAiChatOptions options) { @@ -131,12 +134,7 @@ public class AzureOpenAiChatClient options.setStream(false); logger.trace("Azure ChatCompletionsOptions: {}", options); - ChatCompletions chatCompletions = this.callWithFunctionSupport(options); - - // ChatCompletions chatCompletions = - // this.openAIClient.getChatCompletions(options.getModel(), options); - logger.trace("Azure ChatCompletions: {}", chatCompletions); List generations = chatCompletions.getChoices() @@ -323,7 +321,7 @@ public class AzureOpenAiChatClient mergedAzureOptions.setUser(azureOptions.getUser() != null ? azureOptions.getUser() : springAiOptions.getUser()); mergedAzureOptions - .setModel(azureOptions.getModel() != null ? azureOptions.getModel() : springAiOptions.getModel()); + .setModel(azureOptions.getModel() != null ? azureOptions.getModel() : springAiOptions.getDeploymentName()); return mergedAzureOptions; } @@ -376,8 +374,8 @@ public class AzureOpenAiChatClient mergedAzureOptions.setUser(springAiOptions.getUser()); } - if (springAiOptions.getModel() != null) { - mergedAzureOptions.setModel(springAiOptions.getModel()); + if (springAiOptions.getDeploymentName() != null) { + mergedAzureOptions.setModel(springAiOptions.getDeploymentName()); } return mergedAzureOptions; diff --git a/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatOptions.java b/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatOptions.java index ebedb1b0d..fdc6a2354 100644 --- a/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatOptions.java +++ b/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatOptions.java @@ -120,12 +120,11 @@ public class AzureOpenAiChatOptions implements FunctionCallingOptions, ChatOptio private Double frequencyPenalty; /** - * 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. + * The deployment name as defined in Azure Open AI Studio when creating a deployment + * backed by an Azure OpenAI base model. */ - @JsonProperty(value = "model") - private String model; + @JsonProperty(value = "deployment_name") + private String deploymentName; /** * OpenAI Tool Function Callbacks to register with the ChatClient. For Prompt Options @@ -169,8 +168,8 @@ public class AzureOpenAiChatOptions implements FunctionCallingOptions, ChatOptio this.options = options; } - public Builder withModel(String model) { - this.options.model = model; + public Builder withDeploymentName(String deploymentName) { + this.options.deploymentName = deploymentName; return this; } @@ -298,12 +297,12 @@ public class AzureOpenAiChatOptions implements FunctionCallingOptions, ChatOptio this.frequencyPenalty = frequencyPenalty; } - public String getModel() { - return this.model; + public String getDeploymentName() { + return this.deploymentName; } - public void setModel(String model) { - this.model = model; + public void setDeploymentName(String deploymentName) { + this.deploymentName = deploymentName; } @Override diff --git a/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiEmbeddingClient.java b/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiEmbeddingClient.java index 569a060df..ab2c4c034 100644 --- a/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiEmbeddingClient.java +++ b/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiEmbeddingClient.java @@ -36,6 +36,7 @@ import org.springframework.ai.embedding.EmbeddingResponse; import org.springframework.ai.embedding.EmbeddingResponseMetadata; import org.springframework.ai.model.ModelOptionsUtils; import org.springframework.util.Assert; +import org.springframework.util.StringUtils; public class AzureOpenAiEmbeddingClient extends AbstractEmbeddingClient { @@ -53,7 +54,7 @@ public class AzureOpenAiEmbeddingClient extends AbstractEmbeddingClient { public AzureOpenAiEmbeddingClient(OpenAIClient azureOpenAiClient, MetadataMode metadataMode) { this(azureOpenAiClient, metadataMode, - AzureOpenAiEmbeddingOptions.builder().withModel("text-embedding-ada-002").build()); + AzureOpenAiEmbeddingOptions.builder().withDeploymentName("text-embedding-ada-002").build()); } public AzureOpenAiEmbeddingClient(OpenAIClient azureOpenAiClient, MetadataMode metadataMode, @@ -93,7 +94,7 @@ public class AzureOpenAiEmbeddingClient extends AbstractEmbeddingClient { EmbeddingsOptions toEmbeddingOptions(EmbeddingRequest embeddingRequest) { var azureOptions = new EmbeddingsOptions(embeddingRequest.getInstructions()); if (this.defaultOptions != null) { - azureOptions.setModel(this.defaultOptions.getModel()); + azureOptions.setModel(this.defaultOptions.getDeploymentName()); azureOptions.setUser(this.defaultOptions.getUser()); } if (embeddingRequest.getOptions() != null && !EmbeddingOptions.EMPTY.equals(embeddingRequest.getOptions())) { diff --git a/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiEmbeddingOptions.java b/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiEmbeddingOptions.java index b6dec12ee..84445803d 100644 --- a/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiEmbeddingOptions.java +++ b/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiEmbeddingOptions.java @@ -35,12 +35,13 @@ public class AzureOpenAiEmbeddingOptions implements EmbeddingOptions { private String user; /** - * 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. + * The deployment name as defined in Azure Open AI Studio when creating a deployment + * backed by an Azure OpenAI base model. If using Azure OpenAI library to communicate + * with OpenAI (not Azure OpenAI) then this value will be used as the name of the + * model. The json serialization of this field is 'model'. */ @JsonProperty(value = "model") - private String model; + private String deploymentName; public static Builder builder() { return new Builder(); @@ -55,8 +56,8 @@ public class AzureOpenAiEmbeddingOptions implements EmbeddingOptions { return this; } - public Builder withModel(String model) { - this.options.setModel(model); + public Builder withDeploymentName(String model) { + this.options.setDeploymentName(model); return this; } @@ -74,12 +75,12 @@ public class AzureOpenAiEmbeddingOptions implements EmbeddingOptions { this.user = user; } - public String getModel() { - return this.model; + public String getDeploymentName() { + return this.deploymentName; } - public void setModel(String model) { - this.model = model; + public void setDeploymentName(String deploymentName) { + this.deploymentName = deploymentName; } } diff --git a/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/AzureChatCompletionsOptionsTests.java b/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/AzureChatCompletionsOptionsTests.java index d7638c9ad..70a0ac67e 100644 --- a/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/AzureChatCompletionsOptionsTests.java +++ b/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/AzureChatCompletionsOptionsTests.java @@ -33,7 +33,7 @@ public class AzureChatCompletionsOptionsTests { OpenAIClient mockClient = Mockito.mock(OpenAIClient.class); var client = new AzureOpenAiChatClient(mockClient, - AzureOpenAiChatOptions.builder().withModel("DEFAULT_MODEL").withTemperature(66.6f).build()); + AzureOpenAiChatOptions.builder().withDeploymentName("DEFAULT_MODEL").withTemperature(66.6f).build()); var requestOptions = client.toAzureChatCompletionsOptions(new Prompt("Test message content")); @@ -43,7 +43,7 @@ public class AzureChatCompletionsOptionsTests { assertThat(requestOptions.getTemperature()).isEqualTo(66.6f); requestOptions = client.toAzureChatCompletionsOptions(new Prompt("Test message content", - AzureOpenAiChatOptions.builder().withModel("PROMPT_MODEL").withTemperature(99.9f).build())); + AzureOpenAiChatOptions.builder().withDeploymentName("PROMPT_MODEL").withTemperature(99.9f).build())); assertThat(requestOptions.getMessages()).hasSize(1); diff --git a/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/AzureEmbeddingsOptionsTests.java b/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/AzureEmbeddingsOptionsTests.java index 2894ef0e9..3a5e194d9 100644 --- a/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/AzureEmbeddingsOptionsTests.java +++ b/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/AzureEmbeddingsOptionsTests.java @@ -37,7 +37,10 @@ public class AzureEmbeddingsOptionsTests { OpenAIClient mockClient = Mockito.mock(OpenAIClient.class); var client = new AzureOpenAiEmbeddingClient(mockClient, MetadataMode.EMBED, - AzureOpenAiEmbeddingOptions.builder().withModel("DEFAULT_MODEL").withUser("USER_TEST").build()); + AzureOpenAiEmbeddingOptions.builder() + .withDeploymentName("DEFAULT_MODEL") + .withUser("USER_TEST") + .build()); var requestOptions = client.toEmbeddingOptions(new EmbeddingRequest(List.of("Test message content"), null)); @@ -47,7 +50,10 @@ public class AzureEmbeddingsOptionsTests { assertThat(requestOptions.getUser()).isEqualTo("USER_TEST"); requestOptions = client.toEmbeddingOptions(new EmbeddingRequest(List.of("Test message content"), - AzureOpenAiEmbeddingOptions.builder().withModel("PROMPT_MODEL").withUser("PROMPT_USER").build())); + AzureOpenAiEmbeddingOptions.builder() + .withDeploymentName("PROMPT_MODEL") + .withUser("PROMPT_USER") + .build())); assertThat(requestOptions.getInput()).hasSize(1); diff --git a/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/AzureOpenAiChatClientIT.java b/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/AzureOpenAiChatClientIT.java index a7c25f8b2..a2fa5e24d 100644 --- a/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/AzureOpenAiChatClientIT.java +++ b/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/AzureOpenAiChatClientIT.java @@ -196,7 +196,7 @@ class AzureOpenAiChatClientIT { @Bean public AzureOpenAiChatClient azureOpenAiChatClient(OpenAIClient openAIClient) { return new AzureOpenAiChatClient(openAIClient, - AzureOpenAiChatOptions.builder().withModel("gpt-35-turbo").withMaxTokens(200).build()); + AzureOpenAiChatOptions.builder().withDeploymentName("gpt-35-turbo").withMaxTokens(200).build()); } diff --git a/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/AzureOpenAiChatClientFunctionCallIT.java b/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/AzureOpenAiChatClientFunctionCallIT.java index db86ee501..3e67dc3c6 100644 --- a/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/AzureOpenAiChatClientFunctionCallIT.java +++ b/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/AzureOpenAiChatClientFunctionCallIT.java @@ -58,7 +58,7 @@ class AzureOpenAiChatClientFunctionCallIT { List messages = new ArrayList<>(List.of(userMessage)); var promptOptions = AzureOpenAiChatOptions.builder() - .withModel("gpt-4-0125-preview") + .withDeploymentName("gpt-4-0125-preview") .withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService()) .withName("getCurrentWeather") .withDescription("Get the current weather in a given location") @@ -88,8 +88,10 @@ class AzureOpenAiChatClientFunctionCallIT { @Bean public AzureOpenAiChatClient azureOpenAiChatClient(OpenAIClient openAIClient) { return new AzureOpenAiChatClient(openAIClient, - AzureOpenAiChatOptions.builder().withModel("gpt-35-turbo-0613").withMaxTokens(500).build()); - + AzureOpenAiChatOptions.builder() + .withDeploymentName("gpt-4-0125-preview") + .withMaxTokens(500) + .build()); } } diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/clients/azure-openai-chat.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/clients/azure-openai-chat.adoc index cec77120b..762628efd 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/clients/azure-openai-chat.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/clients/azure-openai-chat.adoc @@ -95,7 +95,7 @@ The prefix `spring.ai.azure.openai.chat` is the property prefix that configures | Property | Description | Default | spring.ai.azure.openai.chat.enabled | Enable Azure OpenAI chat client. | true -| spring.ai.azure.openai.chat.options.model | * In use with Azure, this actually refers to the "Deployment Name" of your model, which you can find at https://oai.azure.com/portal. It's important to note that within an Azure OpenAI deployment, the "Deployment Name" is distinct from the model itself. The confusion around these terms stems from the intention to make the Azure OpenAI client library compatible with the original OpenAI endpoint. The deployment structures offered by Azure OpenAI and Sam Altman's OpenAI differ significantly. To clarify this distinction, we plan to rename this attribute to `deployment-name` in future updates. +| spring.ai.azure.openai.chat.options.deployment-name | * In use with Azure, this refers to the "Deployment Name" of your model, which you can find at https://oai.azure.com/portal. It's important to note that within an Azure OpenAI deployment, the "Deployment Name" is distinct from the model itself. The confusion around these terms stems from the intention to make the Azure OpenAI client library compatible with the original OpenAI endpoint. The deployment structures offered by Azure OpenAI and Sam Altman's OpenAI differ significantly. Deployments model name to provide as part of this completions request. | gpt-35-turbo | spring.ai.azure.openai.chat.options.maxTokens | The maximum number of tokens to generate. | - diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/embeddings/azure-openai-embeddings.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/embeddings/azure-openai-embeddings.adoc index e2e31fcee..40fe967c5 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/embeddings/azure-openai-embeddings.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/embeddings/azure-openai-embeddings.adoc @@ -74,7 +74,7 @@ The prefix `spring.ai.azure.openai.embeddings` is the property prefix that confi | spring.ai.azure.openai.embedding.enabled | Enable Azure OpenAI embedding client. | true | spring.ai.azure.openai.embedding.metadata-mode | Document content extraction mode | EMBED -| spring.ai.azure.openai.embedding.options.model | This is the value of the 'Deployment Name' as presented in the Azure AI Portal | text-embedding-ada-002 +| spring.ai.azure.openai.embedding.options.deployment-name | This is the value of the 'Deployment Name' as presented in the Azure AI Portal | text-embedding-ada-002 | spring.ai.azure.openai.embedding.options.user | An identifier for the caller or end user of the operation. This may be used for tracking or rate-limiting purposes. | - |==== diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/azure/openai/AzureOpenAiChatProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/azure/openai/AzureOpenAiChatProperties.java index 889e7f6c2..5ce9c2669 100644 --- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/azure/openai/AzureOpenAiChatProperties.java +++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/azure/openai/AzureOpenAiChatProperties.java @@ -24,7 +24,7 @@ public class AzureOpenAiChatProperties { public static final String CONFIG_PREFIX = "spring.ai.azure.openai.chat"; - public static final String DEFAULT_CHAT_MODEL = "gpt-35-turbo"; + public static final String DEFAULT_DEPLOYMENT_NAME = "gpt-35-turbo"; private static final Double DEFAULT_TEMPERATURE = 0.7; @@ -35,7 +35,7 @@ public class AzureOpenAiChatProperties { @NestedConfigurationProperty private AzureOpenAiChatOptions options = AzureOpenAiChatOptions.builder() - .withModel(DEFAULT_CHAT_MODEL) + .withDeploymentName(DEFAULT_DEPLOYMENT_NAME) .withTemperature(DEFAULT_TEMPERATURE.floatValue()) .build(); diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/azure/openai/AzureOpenAiEmbeddingProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/azure/openai/AzureOpenAiEmbeddingProperties.java index c53e094d7..e320eec2f 100644 --- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/azure/openai/AzureOpenAiEmbeddingProperties.java +++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/azure/openai/AzureOpenAiEmbeddingProperties.java @@ -33,7 +33,7 @@ public class AzureOpenAiEmbeddingProperties { @NestedConfigurationProperty private AzureOpenAiEmbeddingOptions options = AzureOpenAiEmbeddingOptions.builder() - .withModel("text-embedding-ada-002") + .withDeploymentName("text-embedding-ada-002") .build(); private MetadataMode metadataMode = MetadataMode.EMBED; diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/AzureOpenAiAutoConfigurationPropertyTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/AzureOpenAiAutoConfigurationPropertyTests.java index ebad37add..7f7e5f704 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/AzureOpenAiAutoConfigurationPropertyTests.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/AzureOpenAiAutoConfigurationPropertyTests.java @@ -47,7 +47,7 @@ public class AzureOpenAiAutoConfigurationPropertyTests { assertThat(connectionProperties.getApiKey()).isEqualTo("TEST_API_KEY"); assertThat(connectionProperties.getEndpoint()).isEqualTo("TEST_ENDPOINT"); - assertThat(chatProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ"); + assertThat(chatProperties.getOptions().getDeploymentName()).isEqualTo("MODEL_XYZ"); }); } @@ -80,9 +80,9 @@ public class AzureOpenAiAutoConfigurationPropertyTests { assertThat(connectionProperties.getEndpoint()).isEqualTo("ENDPOINT"); assertThat(connectionProperties.getApiKey()).isEqualTo("API_KEY"); - assertThat(embeddingProperties.getOptions().getModel()).isEqualTo("text-embedding-ada-002"); + assertThat(embeddingProperties.getOptions().getDeploymentName()).isEqualTo("text-embedding-ada-002"); - assertThat(chatProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ"); + assertThat(chatProperties.getOptions().getDeploymentName()).isEqualTo("MODEL_XYZ"); assertThat(chatProperties.getOptions().getFrequencyPenalty()).isEqualTo(-1.5f); assertThat(chatProperties.getOptions().getLogitBias().get("myTokenId")).isEqualTo(-5); assertThat(chatProperties.getOptions().getMaxTokens()).isEqualTo(123);