Refactor QianFan model option builder methods

- Deprecate the builder methods with the prefix `with` and add the new methods
 - Update docs and references
This commit is contained in:
Ilayaperumal Gopinathan
2024-12-17 01:38:04 +00:00
committed by Mark Pollack
parent 071b897951
commit 18bc0cff25
16 changed files with 205 additions and 59 deletions

View File

@@ -104,8 +104,7 @@ public class QianFanChatModel implements ChatModel, StreamingChatModel {
* @throws IllegalArgumentException if QianFanApi is null
*/
public QianFanChatModel(QianFanApi qianFanApi) {
this(qianFanApi,
QianFanChatOptions.builder().withModel(QianFanApi.DEFAULT_CHAT_MODEL).withTemperature(0.7).build());
this(qianFanApi, QianFanChatOptions.builder().model(QianFanApi.DEFAULT_CHAT_MODEL).temperature(0.7).build());
}
/**

View File

@@ -32,6 +32,7 @@ import org.springframework.ai.qianfan.api.QianFanApi;
* frequency penalty, max tokens, etc.
*
* @author Geng Rong
* @author Ilayaperumal Gopinathan
* @since 1.0
* @see ChatOptions
*/
@@ -87,14 +88,14 @@ public class QianFanChatOptions implements ChatOptions {
public static QianFanChatOptions fromOptions(QianFanChatOptions fromOptions) {
return QianFanChatOptions.builder()
.withModel(fromOptions.getModel())
.withFrequencyPenalty(fromOptions.getFrequencyPenalty())
.withMaxTokens(fromOptions.getMaxTokens())
.withPresencePenalty(fromOptions.getPresencePenalty())
.withResponseFormat(fromOptions.getResponseFormat())
.withStop(fromOptions.getStop())
.withTemperature(fromOptions.getTemperature())
.withTopP(fromOptions.getTopP())
.model(fromOptions.getModel())
.frequencyPenalty(fromOptions.getFrequencyPenalty())
.maxTokens(fromOptions.getMaxTokens())
.presencePenalty(fromOptions.getPresencePenalty())
.responseFormat(fromOptions.getResponseFormat())
.stop(fromOptions.getStop())
.temperature(fromOptions.getTemperature())
.topP(fromOptions.getTopP())
.build();
}
@@ -296,41 +297,115 @@ public class QianFanChatOptions implements ChatOptions {
this.options = options;
}
public Builder model(String model) {
this.options.model = model;
return this;
}
public Builder frequencyPenalty(Double frequencyPenalty) {
this.options.frequencyPenalty = frequencyPenalty;
return this;
}
public Builder maxTokens(Integer maxTokens) {
this.options.maxTokens = maxTokens;
return this;
}
public Builder presencePenalty(Double presencePenalty) {
this.options.presencePenalty = presencePenalty;
return this;
}
public Builder responseFormat(QianFanApi.ChatCompletionRequest.ResponseFormat responseFormat) {
this.options.responseFormat = responseFormat;
return this;
}
public Builder stop(List<String> stop) {
this.options.stop = stop;
return this;
}
public Builder temperature(Double temperature) {
this.options.temperature = temperature;
return this;
}
public Builder topP(Double topP) {
this.options.topP = topP;
return this;
}
/**
* @deprecated use {@link #model(String)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withModel(String model) {
this.options.model = model;
return this;
}
/**
* @deprecated use {@link #frequencyPenalty(Double)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withFrequencyPenalty(Double frequencyPenalty) {
this.options.frequencyPenalty = frequencyPenalty;
return this;
}
/**
* @deprecated use {@link #maxTokens(Integer)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withMaxTokens(Integer maxTokens) {
this.options.maxTokens = maxTokens;
return this;
}
/**
* @deprecated use {@link #presencePenalty(Double)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withPresencePenalty(Double presencePenalty) {
this.options.presencePenalty = presencePenalty;
return this;
}
/**
* @deprecated use
* {@link #responseFormat(QianFanApi.ChatCompletionRequest.ResponseFormat)}
* instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withResponseFormat(QianFanApi.ChatCompletionRequest.ResponseFormat responseFormat) {
this.options.responseFormat = responseFormat;
return this;
}
/**
* @deprecated use {@link #stop(List)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withStop(List<String> stop) {
this.options.stop = stop;
return this;
}
/**
* @deprecated use {@link #temperature(Double)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withTemperature(Double temperature) {
this.options.temperature = temperature;
return this;
}
/**
* @deprecated use {@link #topP(Double)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withTopP(Double topP) {
this.options.topP = topP;
return this;

View File

@@ -90,7 +90,7 @@ public class QianFanEmbeddingModel extends AbstractEmbeddingModel {
*/
public QianFanEmbeddingModel(QianFanApi qianFanApi, MetadataMode metadataMode) {
this(qianFanApi, metadataMode,
QianFanEmbeddingOptions.builder().withModel(QianFanApi.DEFAULT_EMBEDDING_MODEL).build());
QianFanEmbeddingOptions.builder().model(QianFanApi.DEFAULT_EMBEDDING_MODEL).build());
}
/**
@@ -206,8 +206,8 @@ public class QianFanEmbeddingModel extends AbstractEmbeddingModel {
}
return QianFanEmbeddingOptions.builder()
.withModel(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getModel(), defaultOptions.getModel()))
.withUser(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getUser(), defaultOptions.getUser()))
.model(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getModel(), defaultOptions.getModel()))
.user(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getUser(), defaultOptions.getUser()))
.build();
}

View File

@@ -28,6 +28,7 @@ import org.springframework.ai.embedding.EmbeddingOptions;
*
* @author Geng Rong
* @author Thomas Vitale
* @author Ilayaperumal Gopinathan
* @since 1.0
*/
@JsonInclude(Include.NON_NULL)
@@ -81,11 +82,29 @@ public class QianFanEmbeddingOptions implements EmbeddingOptions {
this.options = new QianFanEmbeddingOptions();
}
public Builder model(String model) {
this.options.setModel(model);
return this;
}
public Builder user(String user) {
this.options.setUser(user);
return this;
}
/**
* @deprecated use {@link #model(String)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withModel(String model) {
this.options.setModel(model);
return this;
}
/**
* @deprecated use {@link #user(String)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withUser(String user) {
this.options.setUser(user);
return this;

View File

@@ -203,14 +203,13 @@ public class QianFanImageModel implements ImageModel {
}
return QianFanImageOptions.builder()
.withModel(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getModel(), defaultOptions.getModel()))
.withN(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getN(), defaultOptions.getN()))
.withModel(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getModel(), defaultOptions.getModel()))
.withWidth(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getWidth(), defaultOptions.getWidth()))
.withHeight(
ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getHeight(), defaultOptions.getHeight()))
.withStyle(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getStyle(), defaultOptions.getStyle()))
.withUser(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getUser(), defaultOptions.getUser()))
.model(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getModel(), defaultOptions.getModel()))
.N(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getN(), defaultOptions.getN()))
.model(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getModel(), defaultOptions.getModel()))
.width(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getWidth(), defaultOptions.getWidth()))
.height(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getHeight(), defaultOptions.getHeight()))
.style(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getStyle(), defaultOptions.getStyle()))
.user(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getUser(), defaultOptions.getUser()))
.build();
}

View File

@@ -28,6 +28,7 @@ import org.springframework.ai.image.ImageOptions;
* QianFan Image API options. QianFanImageOptions.java
*
* @author Geng Rong
* @author Ilayaperumal Gopinathan
* @since 1.0
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@@ -196,31 +197,85 @@ public final class QianFanImageOptions implements ImageOptions {
this.options = new QianFanImageOptions();
}
public Builder N(Integer n) {
this.options.setN(n);
return this;
}
public Builder model(String model) {
this.options.setModel(model);
return this;
}
public Builder width(Integer width) {
this.options.setWidth(width);
return this;
}
public Builder height(Integer height) {
this.options.setHeight(height);
return this;
}
public Builder style(String style) {
this.options.setStyle(style);
return this;
}
public Builder user(String user) {
this.options.setUser(user);
return this;
}
/**
* @deprecated use {@link #N(Integer)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withN(Integer n) {
this.options.setN(n);
return this;
}
/**
* @deprecated use {@link #model(String)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withModel(String model) {
this.options.setModel(model);
return this;
}
/**
* @deprecated use {@link #width(Integer)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withWidth(Integer width) {
this.options.setWidth(width);
return this;
}
/**
* @deprecated use {@link #height(Integer)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withHeight(Integer height) {
this.options.setHeight(height);
return this;
}
/**
* @deprecated use {@link #style(String)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withStyle(String style) {
this.options.setStyle(style);
return this;
}
/**
* @deprecated use {@link #user(String)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withUser(String user) {
this.options.setUser(user);
return this;

View File

@@ -32,7 +32,7 @@ public class ChatCompletionRequestTests {
public void createRequestWithChatOptions() {
var client = new QianFanChatModel(new QianFanApi("TEST", "TEST"),
QianFanChatOptions.builder().withModel("DEFAULT_MODEL").withTemperature(66.6).build());
QianFanChatOptions.builder().model("DEFAULT_MODEL").temperature(66.6).build());
var request = client.createRequest(new Prompt("Test message content"), false);
@@ -43,7 +43,7 @@ public class ChatCompletionRequestTests {
assertThat(request.temperature()).isEqualTo(66.6);
request = client.createRequest(new Prompt("Test message content",
QianFanChatOptions.builder().withModel("PROMPT_MODEL").withTemperature(99.9).build()), true);
QianFanChatOptions.builder().model("PROMPT_MODEL").temperature(99.9).build()), true);
assertThat(request.messages()).hasSize(1);
assertThat(request.stream()).isTrue();

View File

@@ -71,13 +71,13 @@ public class QianFanChatModelObservationIT {
void observationForChatOperation() {
var options = QianFanChatOptions.builder()
.withModel(QianFanApi.ChatModel.ERNIE_Speed_8K.getValue())
.withFrequencyPenalty(0.0)
.withMaxTokens(2048)
.withPresencePenalty(0.0)
.withStop(List.of("this-is-the-end"))
.withTemperature(0.7)
.withTopP(1.0)
.model(QianFanApi.ChatModel.ERNIE_Speed_8K.getValue())
.frequencyPenalty(0.0)
.maxTokens(2048)
.presencePenalty(0.0)
.stop(List.of("this-is-the-end"))
.temperature(0.7)
.topP(1.0)
.build();
Prompt prompt = new Prompt("Why does a raven look like a desk?", options);
@@ -94,13 +94,13 @@ public class QianFanChatModelObservationIT {
@Test
void observationForStreamingChatOperation() {
var options = QianFanChatOptions.builder()
.withModel(QianFanApi.ChatModel.ERNIE_Speed_8K.getValue())
.withFrequencyPenalty(0.0)
.withMaxTokens(2048)
.withPresencePenalty(0.0)
.withStop(List.of("this-is-the-end"))
.withTemperature(0.7)
.withTopP(1.0)
.model(QianFanApi.ChatModel.ERNIE_Speed_8K.getValue())
.frequencyPenalty(0.0)
.maxTokens(2048)
.presencePenalty(0.0)
.stop(List.of("this-is-the-end"))
.temperature(0.7)
.topP(1.0)
.build();
Prompt prompt = new Prompt("Why does a raven look like a desk?", options);

View File

@@ -63,7 +63,7 @@ public class QianFanEmbeddingModelObservationIT {
@Test
void observationForEmbeddingOperation() {
var options = QianFanEmbeddingOptions.builder()
.withModel(QianFanApi.EmbeddingModel.BGE_LARGE_ZH.getValue())
.model(QianFanApi.EmbeddingModel.BGE_LARGE_ZH.getValue())
.build();
EmbeddingRequest embeddingRequest = new EmbeddingRequest(List.of("Here comes the sun"), options);

View File

@@ -59,10 +59,10 @@ public class QianFanImageModelObservationIT {
@Test
void observationForImageOperation() {
var options = QianFanImageOptions.builder()
.withModel(QianFanImageApi.ImageModel.Stable_Diffusion_XL.getValue())
.withHeight(1024)
.withWidth(1024)
.withStyle("Base")
.model(QianFanImageApi.ImageModel.Stable_Diffusion_XL.getValue())
.height(1024)
.width(1024)
.style("Base")
.build();
var instructions = "Here comes the sun";

View File

@@ -124,8 +124,8 @@ ChatResponse response = chatClient.call(
new Prompt(
"Generate the names of 5 famous pirates.",
QianFanChatOptions.builder()
.withModel(QianFanApi.ChatModel.ERNIE_Speed_8K.getValue())
.withTemperature(0.5)
.model(QianFanApi.ChatModel.ERNIE_Speed_8K.getValue())
.temperature(0.5)
.build()
));
----
@@ -208,9 +208,9 @@ Next, create a `QianFanChatModel` and use it for text generations:
var qianFanApi = new QianFanApi(System.getenv("QIANFAN_API_KEY"), System.getenv("QIANFAN_SECRET_KEY"));
var chatClient = new QianFanChatModel(this.qianFanApi, QianFanChatOptions.builder()
.withModel(QianFanApi.ChatModel.ERNIE_Speed_8K.getValue())
.withTemperature(0.4)
.withMaxTokens(200)
.model(QianFanApi.ChatModel.ERNIE_Speed_8K.getValue())
.temperature(0.4)
.maxTokens(200)
.build());
ChatResponse response = this.chatClient.call(

View File

@@ -120,7 +120,7 @@ For example to override the default model name for a specific request:
EmbeddingResponse embeddingResponse = embeddingClient.call(
new EmbeddingRequest(List.of("Hello World", "World is big and salvation is near"),
QianFanEmbeddingOptions.builder()
.withModel("Different-Embedding-Model-Deployment-Name")
.model("Different-Embedding-Model-Deployment-Name")
.build()));
----
@@ -188,10 +188,9 @@ Next, create an `QianFanEmbeddingModel` instance and use it to compute the simil
----
var qianFanApi = new QianFanApi(System.getenv("MINIMAX_API_KEY"), System.getenv("QIANFAN_SECRET_KEY"));
var embeddingClient = new QianFanEmbeddingModel(qianFanApi)
.withDefaultOptions(QianFanChatOptions.build()
.withModel("bge_large_en")
.build());
var embeddingClient = new QianFanEmbeddingModel(api, MetadataMode.EMBED, QianFanEmbeddingOptions.builder()
.model("bge_large_en")
.build());
EmbeddingResponse embeddingResponse = this.embeddingClient
.embedForResponse(List.of("Hello World", "World is big and salvation is near"));

View File

@@ -110,9 +110,9 @@ For example to override the QianFan specific options such as quality and the num
ImageResponse response = qianFanImageModel.call(
new ImagePrompt("A light cream colored mini golden doodle",
QianFanImageOptions.builder()
.withN(4)
.withHeight(1024)
.withWidth(1024).build())
.N(4)
.height(1024)
.width(1024).build())
);
----

View File

@@ -42,8 +42,8 @@ public class QianFanChatProperties extends QianFanParentProperties {
@NestedConfigurationProperty
private QianFanChatOptions options = QianFanChatOptions.builder()
.withModel(DEFAULT_CHAT_MODEL)
.withTemperature(DEFAULT_TEMPERATURE)
.model(DEFAULT_CHAT_MODEL)
.temperature(DEFAULT_TEMPERATURE)
.build();
public QianFanChatOptions getOptions() {

View File

@@ -41,7 +41,7 @@ public class QianFanEmbeddingProperties extends QianFanParentProperties {
@NestedConfigurationProperty
private QianFanEmbeddingOptions options = QianFanEmbeddingOptions.builder()
.withModel(QianFanApi.DEFAULT_EMBEDDING_MODEL)
.model(QianFanApi.DEFAULT_EMBEDDING_MODEL)
.build();
public QianFanEmbeddingOptions getOptions() {

View File

@@ -42,7 +42,7 @@ public class QianFanImageProperties extends QianFanParentProperties {
* Options for QianFan Image API.
*/
@NestedConfigurationProperty
private QianFanImageOptions options = QianFanImageOptions.builder().withModel(DEFAULT_IMAGE_MODEL).build();
private QianFanImageOptions options = QianFanImageOptions.builder().model(DEFAULT_IMAGE_MODEL).build();
public QianFanImageOptions getOptions() {
return this.options;