Refactor MistralAiChatOptions builder methods

- Refactor the builder methods to remove `with` as the prefix.
- Introduce new methods with updated naming conventions.
- Deprecate the existing `with*` methods to maintain backward compatibility.
- Update MistralAiChatOptions builder documentation
This commit is contained in:
Alexandros Pappas
2024-12-16 18:05:45 +01:00
committed by Ilayaperumal Gopinathan
parent 48bcbd1555
commit de29df7c58
14 changed files with 210 additions and 75 deletions

View File

@@ -111,10 +111,10 @@ public class MistralAiChatModel extends AbstractToolCallSupport implements ChatM
public MistralAiChatModel(MistralAiApi mistralAiApi) {
this(mistralAiApi,
MistralAiChatOptions.builder()
.withTemperature(0.7)
.withTopP(1.0)
.withSafePrompt(false)
.withModel(MistralAiApi.ChatModel.OPEN_MISTRAL_7B.getValue())
.temperature(0.7)
.topP(1.0)
.safePrompt(false)
.model(MistralAiApi.ChatModel.OPEN_MISTRAL_7B.getValue())
.build());
}
@@ -417,7 +417,7 @@ public class MistralAiChatModel extends AbstractToolCallSupport implements ChatM
if (!CollectionUtils.isEmpty(functionsForThisRequest)) {
request = ModelOptionsUtils.merge(
MistralAiChatOptions.builder().withTools(this.getFunctionTools(functionsForThisRequest)).build(),
MistralAiChatOptions.builder().tools(this.getFunctionTools(functionsForThisRequest)).build(),
request, ChatCompletionRequest.class);
}

View File

@@ -41,6 +41,7 @@ import org.springframework.util.Assert;
* @author Ricken Bazolo
* @author Christian Tzolov
* @author Thomas Vitale
* @author Alexandros Pappas
* @since 0.8.1
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@@ -145,20 +146,20 @@ public class MistralAiChatOptions implements FunctionCallingOptions {
}
public static MistralAiChatOptions fromOptions(MistralAiChatOptions fromOptions) {
return builder().withModel(fromOptions.getModel())
.withMaxTokens(fromOptions.getMaxTokens())
.withSafePrompt(fromOptions.getSafePrompt())
.withRandomSeed(fromOptions.getRandomSeed())
.withTemperature(fromOptions.getTemperature())
.withTopP(fromOptions.getTopP())
.withResponseFormat(fromOptions.getResponseFormat())
.withStop(fromOptions.getStop())
.withTools(fromOptions.getTools())
.withToolChoice(fromOptions.getToolChoice())
.withFunctionCallbacks(fromOptions.getFunctionCallbacks())
.withFunctions(fromOptions.getFunctions())
.withProxyToolCalls(fromOptions.getProxyToolCalls())
.withToolContext(fromOptions.getToolContext())
return builder().model(fromOptions.getModel())
.maxTokens(fromOptions.getMaxTokens())
.safePrompt(fromOptions.getSafePrompt())
.randomSeed(fromOptions.getRandomSeed())
.temperature(fromOptions.getTemperature())
.topP(fromOptions.getTopP())
.responseFormat(fromOptions.getResponseFormat())
.stop(fromOptions.getStop())
.tools(fromOptions.getTools())
.toolChoice(fromOptions.getToolChoice())
.functionCallbacks(fromOptions.getFunctionCallbacks())
.functions(fromOptions.getFunctions())
.proxyToolCalls(fromOptions.getProxyToolCalls())
.toolContext(fromOptions.getToolContext())
.build();
}
@@ -357,84 +358,84 @@ public class MistralAiChatOptions implements FunctionCallingOptions {
private final MistralAiChatOptions options = new MistralAiChatOptions();
public Builder withModel(String model) {
public Builder model(String model) {
this.options.setModel(model);
return this;
}
public Builder withModel(MistralAiApi.ChatModel chatModel) {
public Builder model(MistralAiApi.ChatModel chatModel) {
this.options.setModel(chatModel.getName());
return this;
}
public Builder withMaxTokens(Integer maxTokens) {
public Builder maxTokens(Integer maxTokens) {
this.options.setMaxTokens(maxTokens);
return this;
}
public Builder withSafePrompt(Boolean safePrompt) {
public Builder safePrompt(Boolean safePrompt) {
this.options.setSafePrompt(safePrompt);
return this;
}
public Builder withRandomSeed(Integer randomSeed) {
public Builder randomSeed(Integer randomSeed) {
this.options.setRandomSeed(randomSeed);
return this;
}
public Builder withStop(List<String> stop) {
public Builder stop(List<String> stop) {
this.options.setStop(stop);
return this;
}
public Builder withTemperature(Double temperature) {
public Builder temperature(Double temperature) {
this.options.setTemperature(temperature);
return this;
}
public Builder withTopP(Double topP) {
public Builder topP(Double topP) {
this.options.setTopP(topP);
return this;
}
public Builder withResponseFormat(ResponseFormat responseFormat) {
public Builder responseFormat(ResponseFormat responseFormat) {
this.options.responseFormat = responseFormat;
return this;
}
public Builder withTools(List<FunctionTool> tools) {
public Builder tools(List<FunctionTool> tools) {
this.options.tools = tools;
return this;
}
public Builder withToolChoice(ToolChoice toolChoice) {
public Builder toolChoice(ToolChoice toolChoice) {
this.options.toolChoice = toolChoice;
return this;
}
public Builder withFunctionCallbacks(List<FunctionCallback> functionCallbacks) {
public Builder functionCallbacks(List<FunctionCallback> functionCallbacks) {
this.options.functionCallbacks = functionCallbacks;
return this;
}
public Builder withFunctions(Set<String> functionNames) {
public Builder functions(Set<String> functionNames) {
Assert.notNull(functionNames, "Function names must not be null");
this.options.functions = functionNames;
return this;
}
public Builder withFunction(String functionName) {
public Builder function(String functionName) {
Assert.hasText(functionName, "Function name must not be empty");
this.options.functions.add(functionName);
return this;
}
public Builder withProxyToolCalls(Boolean proxyToolCalls) {
public Builder proxyToolCalls(Boolean proxyToolCalls) {
this.options.proxyToolCalls = proxyToolCalls;
return this;
}
public Builder withToolContext(Map<String, Object> toolContext) {
public Builder toolContext(Map<String, Object> toolContext) {
if (this.options.toolContext == null) {
this.options.toolContext = toolContext;
}
@@ -444,6 +445,134 @@ public class MistralAiChatOptions implements FunctionCallingOptions {
return this;
}
/**
* @deprecated use {@link #model(String)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withModel(String model) {
return model(model);
}
/**
* @deprecated use {@link #model(MistralAiApi.ChatModel)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withModel(MistralAiApi.ChatModel chatModel) {
return model(chatModel);
}
/**
* @deprecated use {@link #maxTokens(Integer)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withMaxTokens(Integer maxTokens) {
return maxTokens(maxTokens);
}
/**
* @deprecated use {@link #safePrompt(Boolean)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withSafePrompt(Boolean safePrompt) {
return safePrompt(safePrompt);
}
/**
* @deprecated use {@link #randomSeed(Integer)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withRandomSeed(Integer randomSeed) {
return randomSeed(randomSeed);
}
/**
* @deprecated use {@link #stop(List)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withStop(List<String> stop) {
return stop(stop);
}
/**
* @deprecated use {@link #temperature(Double)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withTemperature(Double temperature) {
return temperature(temperature);
}
/**
* @deprecated use {@link #topP(Double)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withTopP(Double topP) {
return topP(topP);
}
/**
* @deprecated use {@link #responseFormat(ResponseFormat)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withResponseFormat(ResponseFormat responseFormat) {
return responseFormat(responseFormat);
}
/**
* @deprecated use {@link #tools(List)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withTools(List<FunctionTool> tools) {
return tools(tools);
}
/**
* @deprecated use {@link #toolChoice(ToolChoice)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withToolChoice(ToolChoice toolChoice) {
return toolChoice(toolChoice);
}
/**
* @deprecated use {@link #functionCallbacks(List)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withFunctionCallbacks(List<FunctionCallback> functionCallbacks) {
return functionCallbacks(functionCallbacks);
}
/**
* @deprecated use {@link #functions(Set)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withFunctions(Set<String> functionNames) {
return functions(functionNames);
}
/**
* @deprecated use {@link #function(String)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withFunction(String functionName) {
return function(functionName);
}
/**
* @deprecated use {@link #proxyToolCalls(Boolean)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withProxyToolCalls(Boolean proxyToolCalls) {
return proxyToolCalls(proxyToolCalls);
}
/**
* @deprecated use {@link #toolContext(Map)} instead.
*/
@Deprecated(forRemoval = true, since = "1.0.0-M5")
public Builder withToolContext(Map<String, Object> toolContext) {
return toolContext(toolContext);
}
public MistralAiChatOptions build() {
return this.options;
}

View File

@@ -223,7 +223,7 @@ class MistralAiChatClientIT {
// @formatter:off
String response = ChatClient.create(this.chatModel).prompt()
.options(MistralAiChatOptions.builder().withModel(MistralAiApi.ChatModel.SMALL).withToolChoice(ToolChoice.AUTO).build())
.options(MistralAiChatOptions.builder().model(MistralAiApi.ChatModel.SMALL).toolChoice(ToolChoice.AUTO).build())
.user(u -> u.text("What's the weather like in San Francisco, Tokyo, and Paris? Use parallel function calling if required. Response should be in Celsius."))
.functions(FunctionCallback.builder()
.function("getCurrentWeather", new MockWeatherService())
@@ -246,7 +246,7 @@ class MistralAiChatClientIT {
// @formatter:off
String response = ChatClient.builder(this.chatModel)
.defaultOptions(MistralAiChatOptions.builder().withModel(MistralAiApi.ChatModel.SMALL).build())
.defaultOptions(MistralAiChatOptions.builder().model(MistralAiApi.ChatModel.SMALL).build())
.defaultFunctions(FunctionCallback.builder()
.function("getCurrentWeather", new MockWeatherService())
.description("Get the weather in location")
@@ -269,7 +269,7 @@ class MistralAiChatClientIT {
// @formatter:off
Flux<String> response = ChatClient.create(this.chatModel).prompt()
.options(MistralAiChatOptions.builder().withModel(MistralAiApi.ChatModel.SMALL).build())
.options(MistralAiChatOptions.builder().model(MistralAiApi.ChatModel.SMALL).build())
.user("What's the weather like in San Francisco, Tokyo, and Paris? Use parallel function calling if required. Response should be in Celsius.")
.functions(FunctionCallback.builder()
.function("getCurrentWeather", new MockWeatherService())
@@ -295,7 +295,7 @@ class MistralAiChatClientIT {
// String model = MistralAiApi.ChatModel.PIXTRAL_LARGE.getName();
// @formatter:off
ChatResponse response = ChatClient.create(this.chatModel).prompt()
.options(MistralAiChatOptions.builder().withModel(model).build())
.options(MistralAiChatOptions.builder().model(model).build())
.user("Tell me about 3 famous pirates from the Golden Age of Piracy and what they did")
.call()
.chatResponse();

View File

@@ -27,6 +27,7 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ricken Bazolo
* @author Alexandros Pappas
* @since 0.8.1
*/
@SpringBootTest(classes = MistralAiTestConfiguration.class)
@@ -51,7 +52,7 @@ public class MistralAiChatCompletionRequestTest {
@Test
void chatCompletionRequestWithOptionsTest() {
var options = MistralAiChatOptions.builder().withTemperature(0.5).withTopP(0.8).build();
var options = MistralAiChatOptions.builder().temperature(0.5).topP(0.8).build();
var request = this.chatModel.createRequest(new Prompt("test content", options), true);

View File

@@ -53,6 +53,7 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Christian Tzolov
* @author Alexandros Pappas
* @since 0.8.1
*/
@SpringBootTest(classes = MistralAiTestConfiguration.class)
@@ -192,8 +193,8 @@ class MistralAiChatModelIT {
List<Message> messages = new ArrayList<>(List.of(userMessage));
var promptOptions = MistralAiChatOptions.builder()
.withModel(MistralAiApi.ChatModel.SMALL.getValue())
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.model(MistralAiApi.ChatModel.SMALL.getValue())
.functionCallbacks(List.of(FunctionCallback.builder()
.function("getCurrentWeather", new MockWeatherService())
.description("Get the weather in location")
.inputType(MockWeatherService.Request.class)
@@ -218,8 +219,8 @@ class MistralAiChatModelIT {
List<Message> messages = new ArrayList<>(List.of(userMessage));
var promptOptions = MistralAiChatOptions.builder()
.withModel(MistralAiApi.ChatModel.SMALL.getValue())
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.model(MistralAiApi.ChatModel.SMALL.getValue())
.functionCallbacks(List.of(FunctionCallback.builder()
.function("getCurrentWeather", new MockWeatherService())
.description("Get the weather in location")
.inputType(MockWeatherService.Request.class)

View File

@@ -50,6 +50,7 @@ import static org.springframework.ai.chat.observation.ChatModelObservationDocume
* Integration tests for observation instrumentation in {@link MistralAiChatModel}.
*
* @author Thomas Vitale
* @author Alexandros Pappas
*/
@SpringBootTest(classes = MistralAiChatModelObservationIT.Config.class)
@EnabledIfEnvironmentVariable(named = "MISTRAL_AI_API_KEY", matches = ".+")
@@ -69,11 +70,11 @@ public class MistralAiChatModelObservationIT {
@Test
void observationForChatOperation() {
var options = MistralAiChatOptions.builder()
.withModel(MistralAiApi.ChatModel.OPEN_MISTRAL_7B.getValue())
.withMaxTokens(2048)
.withStop(List.of("this-is-the-end"))
.withTemperature(0.7)
.withTopP(1.0)
.model(MistralAiApi.ChatModel.OPEN_MISTRAL_7B.getValue())
.maxTokens(2048)
.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);
@@ -90,11 +91,11 @@ public class MistralAiChatModelObservationIT {
@Test
void observationForStreamingChatOperation() {
var options = MistralAiChatOptions.builder()
.withModel(MistralAiApi.ChatModel.OPEN_MISTRAL_7B.getValue())
.withMaxTokens(2048)
.withStop(List.of("this-is-the-end"))
.withTemperature(0.7)
.withTopP(1.0)
.model(MistralAiApi.ChatModel.OPEN_MISTRAL_7B.getValue())
.maxTokens(2048)
.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

@@ -55,6 +55,7 @@ import static org.mockito.BDDMockito.given;
/**
* @author Christian Tzolov
* @author Thomas Vitale
* @author Alexandros Pappas
*/
@SuppressWarnings("unchecked")
@ExtendWith(MockitoExtension.class)
@@ -78,10 +79,10 @@ public class MistralAiRetryTests {
this.chatModel = new MistralAiChatModel(this.mistralAiApi,
MistralAiChatOptions.builder()
.withTemperature(0.7)
.withTopP(1.0)
.withSafePrompt(false)
.withModel(MistralAiApi.ChatModel.OPEN_MISTRAL_7B.getValue())
.temperature(0.7)
.topP(1.0)
.safePrompt(false)
.model(MistralAiApi.ChatModel.OPEN_MISTRAL_7B.getValue())
.build(),
null, this.retryTemplate);
this.embeddingModel = new MistralAiEmbeddingModel(this.mistralAiApi, MetadataMode.EMBED,

View File

@@ -44,7 +44,7 @@ public class MistralAiTestConfiguration {
@Bean
public MistralAiChatModel mistralAiChatModel(MistralAiApi mistralAiApi) {
return new MistralAiChatModel(mistralAiApi,
MistralAiChatOptions.builder().withModel(MistralAiApi.ChatModel.OPEN_MIXTRAL_7B.getValue()).build());
MistralAiChatOptions.builder().model(MistralAiApi.ChatModel.OPEN_MIXTRAL_7B.getValue()).build());
}
}

View File

@@ -153,7 +153,7 @@ MistralAiChatModel chatModel = ...
UserMessage userMessage = new UserMessage("What's the weather like in Paris?");
ChatResponse response = this.chatModel.call(new Prompt(this.userMessage,
MistralAiChatOptions.builder().withFunction("CurrentWeather").build())); // Enable the function
MistralAiChatOptions.builder().function("CurrentWeather").build())); // Enable the function
logger.info("Response: {}", response);
----
@@ -173,7 +173,7 @@ MistralAiChatModel chatModel = ...
UserMessage userMessage = new UserMessage("What's the weather like in Paris?");
var promptOptions = MistralAiChatOptions.builder()
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.functionCallbacks(List.of(FunctionCallback.builder()
.function("CurrentWeather", new MockWeatherService()) // (1) function name and instance
.description("Get the weather in location") // (2) function description
.inputType(MockWeatherService.Request.class) // (3) function signature

View File

@@ -126,8 +126,8 @@ ChatResponse response = chatModel.call(
new Prompt(
"Generate the names of 5 famous pirates.",
MistralAiChatOptions.builder()
.withModel(MistralAiApi.ChatModel.LARGE.getValue())
.withTemperature(0.5)
.model(MistralAiApi.ChatModel.LARGE.getValue())
.temperature(0.5)
.build()
));
----
@@ -222,9 +222,9 @@ Next, create a `MistralAiChatModel` and use it for text generations:
var mistralAiApi = new MistralAiApi(System.getenv("MISTRAL_AI_API_KEY"));
var chatModel = new MistralAiChatModel(this.mistralAiApi, MistralAiChatOptions.builder()
.withModel(MistralAiApi.ChatModel.LARGE.getValue())
.withTemperature(0.4)
.withMaxTokens(200)
.model(MistralAiApi.ChatModel.LARGE.getValue())
.temperature(0.4)
.maxTokens(200)
.build());
ChatResponse response = this.chatModel.call(

View File

@@ -27,6 +27,7 @@ import org.springframework.boot.context.properties.NestedConfigurationProperty;
* @author Ricken Bazolo
* @author Christian Tzolov
* @author Thomas Vitale
* @author Alexandros Pappas
* @since 0.8.1
*/
@ConfigurationProperties(MistralAiChatProperties.CONFIG_PREFIX)
@@ -49,10 +50,10 @@ public class MistralAiChatProperties extends MistralAiParentProperties {
@NestedConfigurationProperty
private MistralAiChatOptions options = MistralAiChatOptions.builder()
.withModel(DEFAULT_CHAT_MODEL)
.withTemperature(DEFAULT_TEMPERATURE)
.withSafePrompt(!IS_ENABLED)
.withTopP(DEFAULT_TOP_P)
.model(DEFAULT_CHAT_MODEL)
.temperature(DEFAULT_TEMPERATURE)
.safePrompt(!IS_ENABLED)
.topP(DEFAULT_TOP_P)
.build();
public MistralAiChatProperties() {

View File

@@ -68,8 +68,8 @@ class PaymentStatusBeanIT {
ChatResponse response = chatModel
.call(new Prompt(List.of(new UserMessage("What's the status of my transaction with id T1001?")),
MistralAiChatOptions.builder()
.withFunction("retrievePaymentStatus")
.withFunction("retrievePaymentDate")
.function("retrievePaymentStatus")
.function("retrievePaymentDate")
.build()));
logger.info("Response: {}", response);

View File

@@ -64,7 +64,7 @@ public class PaymentStatusPromptIT {
UserMessage userMessage = new UserMessage("What's the status of my transaction with id T1001?");
var promptOptions = MistralAiChatOptions.builder()
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.functionCallbacks(List.of(FunctionCallback.builder()
.function("retrievePaymentStatus",
(Transaction transaction) -> new Status(DATA.get(transaction).status()))
.description("Get payment status of a transaction")

View File

@@ -46,6 +46,7 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Christian Tzolov
* @author Alexandros Pappas
* @since 0.8.1
*/
@EnabledIfEnvironmentVariable(named = "MISTRAL_AI_API_KEY", matches = ".*")
@@ -71,8 +72,8 @@ public class WeatherServicePromptIT {
// Paris?");
var promptOptions = MistralAiChatOptions.builder()
.withToolChoice(ToolChoice.AUTO)
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.toolChoice(ToolChoice.AUTO)
.functionCallbacks(List.of(FunctionCallback.builder()
.function("CurrentWeatherService", new MyWeatherService())
.description("Get the current weather in requested location")
.inputType(MyWeatherService.Request.class)