feat(anthropic): update to Claude 3.7 Sonnet and refactor API

- Add support for Claude 3.7 Sonnet model and make it the default
- Rename function-related APIs to tool-related APIs for consistency:
- Change functionCallbacks to toolCallbacks
- Change function to toolNames
- Replace FunctionCallingOptions with ToolCallingChatOptions
- Refactor AnthropicChatModel instantiation to use builder pattern
- Update tests to use latest model versions instead of dated versions

Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
This commit is contained in:
Christian Tzolov
2025-02-25 15:04:02 +01:00
committed by Mark Pollack
parent c91163b6a3
commit 3dfea48249
8 changed files with 26 additions and 20 deletions

View File

@@ -93,7 +93,7 @@ import org.springframework.util.StringUtils;
*/
public class AnthropicChatModel extends AbstractToolCallSupport implements ChatModel {
public static final String DEFAULT_MODEL_NAME = AnthropicApi.ChatModel.CLAUDE_3_5_SONNET.getValue();
public static final String DEFAULT_MODEL_NAME = AnthropicApi.ChatModel.CLAUDE_3_7_SONNET.getValue();
public static final Integer DEFAULT_MAX_TOKENS = 500;

View File

@@ -227,6 +227,11 @@ public class AnthropicApi {
public enum ChatModel implements ChatModelDescription {
// @formatter:off
/**
* The claude-3-7-sonnet-latest model.
*/
CLAUDE_3_7_SONNET("claude-3-7-sonnet-latest"),
/**
* The claude-3-5-sonnet-20241022 model.
*/

View File

@@ -89,8 +89,8 @@ class AnthropicChatModelIT {
}
@ParameterizedTest(name = "{0} : {displayName} ")
@ValueSource(strings = { "claude-3-opus-20240229", "claude-3-sonnet-20240229", "claude-3-haiku-20240307",
"claude-3-5-sonnet-20241022" })
@ValueSource(strings = { "claude-3-7-sonnet-latest", "claude-3-5-sonnet-latest", "claude-3-5-haiku-latest",
"claude-3-opus-latest" })
void roleTest(String modelName) {
UserMessage userMessage = new UserMessage(
"Tell me about 3 famous pirates from the Golden Age of Piracy and why they did.");
@@ -275,11 +275,11 @@ class AnthropicChatModelIT {
var promptOptions = AnthropicChatOptions.builder()
.model(AnthropicApi.ChatModel.CLAUDE_3_OPUS.getName())
.functionCallbacks(List.of(FunctionToolCallback.builder("getCurrentWeather", new MockWeatherService())
.toolCallbacks(FunctionToolCallback.builder("getCurrentWeather", new MockWeatherService())
.description(
"Get the weather in location. Return temperature in 36°F or 36°C format. Use multi-turn if needed.")
.inputType(MockWeatherService.Request.class)
.build()))
.build())
.build();
ChatResponse response = this.chatModel.call(new Prompt(messages, promptOptions));
@@ -307,11 +307,11 @@ class AnthropicChatModelIT {
var promptOptions = AnthropicChatOptions.builder()
.model(AnthropicApi.ChatModel.CLAUDE_3_5_SONNET.getName())
.functionCallbacks(List.of(FunctionToolCallback.builder("getCurrentWeather", new MockWeatherService())
.toolCallbacks(FunctionToolCallback.builder("getCurrentWeather", new MockWeatherService())
.description(
"Get the weather in location. Return temperature in 36°F or 36°C format. Use multi-turn if needed.")
.inputType(MockWeatherService.Request.class)
.build()))
.build())
.build();
Flux<ChatResponse> response = this.chatModel.stream(new Prompt(messages, promptOptions));
@@ -337,11 +337,11 @@ class AnthropicChatModelIT {
var promptOptions = AnthropicChatOptions.builder()
.model(AnthropicApi.ChatModel.CLAUDE_3_5_SONNET.getName())
.functionCallbacks(List.of(FunctionToolCallback.builder("getCurrentWeather", new MockWeatherService())
.toolCallbacks(FunctionToolCallback.builder("getCurrentWeather", new MockWeatherService())
.description(
"Get the weather in location. Return temperature in 36°F or 36°C format. Use multi-turn if needed.")
.inputType(MockWeatherService.Request.class)
.build()))
.build())
.build();
Flux<ChatResponse> responseFlux = this.chatModel.stream(new Prompt(messages, promptOptions));
@@ -410,7 +410,7 @@ class AnthropicChatModelIT {
@Bean
public AnthropicChatModel openAiChatModel(AnthropicApi api) {
return new AnthropicChatModel(api);
return AnthropicChatModel.builder().anthropicApi(api).build();
}
}

View File

@@ -40,8 +40,7 @@ public class AnthropicTestConfiguration {
@Bean
public AnthropicChatModel anthropicChatModel(AnthropicApi api) {
AnthropicChatModel anthropicChatModel = new AnthropicChatModel(api);
return anthropicChatModel;
return AnthropicChatModel.builder().anthropicApi(api).build();
}
}

View File

@@ -33,8 +33,10 @@ public class ChatCompletionRequestTests {
@Test
public void createRequestWithChatOptions() {
var client = new AnthropicChatModel(new AnthropicApi("TEST"),
AnthropicChatOptions.builder().model("DEFAULT_MODEL").temperature(66.6).build());
var client = AnthropicChatModel.builder()
.anthropicApi(new AnthropicApi("TEST"))
.defaultOptions(AnthropicChatOptions.builder().model("DEFAULT_MODEL").temperature(66.6).build())
.build();
var prompt = client.buildRequestPrompt(new Prompt("Test message content"));

View File

@@ -102,7 +102,7 @@ The prefix `spring.ai.anthropic.chat` is the property prefix that lets you confi
| Property | Description | Default
| spring.ai.anthropic.chat.enabled | Enable Anthropic chat model. | true
| spring.ai.anthropic.chat.options.model | This is the Anthropic Chat model to use. Supports: `claude-3-5-sonnet-20241022`, `claude-3-opus-20240229`, `claude-3-sonnet-20240229`, `claude-3-haiku-20240307` and the legacy `claude-2.1`, `claude-2.0` and `claude-instant-1.2` models. | `claude-3-opus-20240229`
| spring.ai.anthropic.chat.options.model | This is the Anthropic Chat model to use. Supports: `claude-3-7-sonnet-latest`, `claude-3-5-sonnet-latest`, `claude-3-opus-20240229`, `claude-3-sonnet-20240229`, `claude-3-haiku-20240307` and the legacy `claude-2.1`, `claude-2.0` and `claude-instant-1.2` models. | `claude-3-7-sonnet-latest`
| spring.ai.anthropic.chat.options.temperature | The sampling temperature to use that controls the apparent creativity of generated completions. Higher values will make output more random while lower values will make results more focused and deterministic. It is not recommended to modify temperature and top_p for the same completions request as the interaction of these two settings is difficult to predict. | 0.8
| spring.ai.anthropic.chat.options.max-tokens | The maximum number of tokens to generate in the chat completion. The total length of input tokens and generated tokens is limited by the model's context length. | 500
| spring.ai.anthropic.chat.options.stop-sequence | Custom text sequences that will cause the model to stop generating. Our models will normally stop when they have naturally completed their turn, which will result in a response stop_reason of "end_turn". If you want the model to stop generating when it encounters custom strings of text, you can use the stop_sequences parameter. If the model encounters one of the custom sequences, the response stop_reason value will be "stop_sequence" and the response stop_sequence value will contain the matched stop sequence. | -

View File

@@ -33,7 +33,7 @@ import org.springframework.ai.autoconfigure.anthropic.tool.MockWeatherService.Re
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.function.FunctionCallingOptions;
import org.springframework.ai.model.tool.ToolCallingChatOptions;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
@@ -66,14 +66,14 @@ class FunctionCallWithFunctionBeanIT {
"What's the weather like in San Francisco, in Paris, France and in Tokyo, Japan? Return the temperature in Celsius.");
ChatResponse response = chatModel.call(new Prompt(List.of(userMessage),
AnthropicChatOptions.builder().function("weatherFunction").build()));
AnthropicChatOptions.builder().toolNames("weatherFunction").build()));
logger.info("Response: {}", response);
assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
response = chatModel.call(new Prompt(List.of(userMessage),
AnthropicChatOptions.builder().function("weatherFunction3").build()));
AnthropicChatOptions.builder().toolNames("weatherFunction3").build()));
logger.info("Response: {}", response);
@@ -96,7 +96,7 @@ class FunctionCallWithFunctionBeanIT {
"What's the weather like in San Francisco, in Paris, France and in Tokyo, Japan? Return the temperature in Celsius.");
ChatResponse response = chatModel.call(new Prompt(List.of(userMessage),
FunctionCallingOptions.builder().function("weatherFunction").build()));
ToolCallingChatOptions.builder().toolNames("weatherFunction").build()));
logger.info("Response: {}", response);

View File

@@ -58,7 +58,7 @@ public class FunctionCallWithPromptFunctionIT {
"What's the weather like in San Francisco, in Paris and in Tokyo? Return the temperature in Celsius.");
var promptOptions = AnthropicChatOptions.builder()
.functionCallbacks(
.toolCallbacks(
List.of(FunctionToolCallback.builder("CurrentWeatherService", new MockWeatherService())
.description("Get the weather in location. Return temperature in 36°F or 36°C format.")
.inputType(MockWeatherService.Request.class)