Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@

class LangChain4jIntegrationTest {

public static final String CLAUDE_3_7_SONNET_20250219 = "claude-3-7-sonnet-20250219";
public static final String CLAUDE_4_6_SONNET = "claude-sonnet-4-6";
public static final String GEMINI_2_0_FLASH = "gemini-2.0-flash";
public static final String GPT_4_O_MINI = "gpt-4o-mini";

Expand All @@ -55,14 +55,14 @@ void testSimpleAgent() {
AnthropicChatModel claudeModel =
AnthropicChatModel.builder()
.apiKey(System.getenv("ANTHROPIC_API_KEY"))
.modelName(CLAUDE_3_7_SONNET_20250219)
.modelName(CLAUDE_4_6_SONNET)
.build();

LlmAgent agent =
LlmAgent.builder()
.name("science-app")
.description("Science teacher agent")
.model(new LangChain4j(claudeModel, CLAUDE_3_7_SONNET_20250219))
.model(new LangChain4j(claudeModel, CLAUDE_4_6_SONNET))
.instruction(
"""
You are a helpful science teacher that explains science concepts
Expand Down Expand Up @@ -91,14 +91,14 @@ void testSingleAgentWithTools() {
AnthropicChatModel claudeModel =
AnthropicChatModel.builder()
.apiKey(System.getenv("ANTHROPIC_API_KEY"))
.modelName(CLAUDE_3_7_SONNET_20250219)
.modelName(CLAUDE_4_6_SONNET)
.build();

BaseAgent agent =
LlmAgent.builder()
.name("friendly-weather-app")
.description("Friend agent that knows about the weather")
.model(new LangChain4j(claudeModel, CLAUDE_3_7_SONNET_20250219))
.model(new LangChain4j(claudeModel, CLAUDE_4_6_SONNET))
.instruction(
"""
You are a friendly assistant.
Expand Down Expand Up @@ -155,7 +155,7 @@ void testSingleAgentWithTools() {
List<Part> partsThree = contentThree.parts().get();
assertEquals(1, partsThree.size());
assertTrue(partsThree.get(0).text().isPresent());
assertTrue(partsThree.get(0).text().get().contains("beautiful"));
assertTrue(partsThree.get(0).text().get().contains("sunny"));
}

@Test
Expand Down Expand Up @@ -352,10 +352,10 @@ void testSimpleStreamingResponse() {
AnthropicStreamingChatModel claudeStreamingModel =
AnthropicStreamingChatModel.builder()
.apiKey(System.getenv("ANTHROPIC_API_KEY"))
.modelName(CLAUDE_3_7_SONNET_20250219)
.modelName(CLAUDE_4_6_SONNET)
.build();

LangChain4j lc4jClaude = new LangChain4j(claudeStreamingModel, CLAUDE_3_7_SONNET_20250219);
LangChain4j lc4jClaude = new LangChain4j(claudeStreamingModel, CLAUDE_4_6_SONNET);

// when
Flowable<LlmResponse> responses =
Expand Down
2 changes: 1 addition & 1 deletion contrib/spring-ai/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<description>Spring AI integration for the Agent Development Kit.</description>

<properties>
<spring-ai.version>2.0.0-M2</spring-ai.version>
<spring-ai.version>2.0.0-M3</spring-ai.version>
<testcontainers.version>1.21.3</testcontainers.version>
</properties>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.springframework.ai.anthropic.AnthropicChatModel;
import org.springframework.ai.anthropic.AnthropicChatOptions;
import org.springframework.ai.anthropic.api.AnthropicApi;

/**
* Integration tests with real Anthropic API.
Expand All @@ -53,10 +52,14 @@ void testSimpleAgentWithRealAnthropicApi() throws InterruptedException {
Thread.sleep(2000);

// Create Anthropic model using Spring AI's builder pattern
AnthropicApi anthropicApi =
AnthropicApi.builder().apiKey(System.getenv("ANTHROPIC_API_KEY")).build();
AnthropicChatModel anthropicModel =
AnthropicChatModel.builder().anthropicApi(anthropicApi).build();
var options =
AnthropicChatOptions.builder()
.model(CLAUDE_MODEL)
.maxTokens(1024)
.apiKey(System.getenv("ANTHROPIC_API_KEY"))
.build();

AnthropicChatModel anthropicModel = AnthropicChatModel.builder().options(options).build();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The builder method .options() is incorrect for AnthropicChatModel.builder(). According to the Spring AI 2.0.0-M3 API, you should use .withDefaultOptions(). This will cause a compilation error.

Suggested change
AnthropicChatModel anthropicModel = AnthropicChatModel.builder().options(options).build();
AnthropicChatModel anthropicModel = AnthropicChatModel.builder().withDefaultOptions(options).build();

Comment on lines +55 to +62
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for creating a default AnthropicChatModel is duplicated across several tests (testSimpleAgentWithRealAnthropicApi, testStreamingWithRealAnthropicApi, etc.). To improve maintainability and reduce redundancy, consider extracting this into a private helper method.

For example, you could add a method like this to the class:

private AnthropicChatModel createDefaultAnthropicChatModel() {
  var options =
      AnthropicChatOptions.builder()
          .model(CLAUDE_MODEL)
          .maxTokens(1024)
          .apiKey(System.getenv("ANTHROPIC_API_KEY"))
          .build();
  // Note: .withDefaultOptions() should be used instead of .options()
  return AnthropicChatModel.builder().withDefaultOptions(options).build();
}

Then you could replace this block in each test with:

AnthropicChatModel anthropicModel = createDefaultAnthropicChatModel();


// Wrap with SpringAI
SpringAI springAI = new SpringAI(anthropicModel, CLAUDE_MODEL);
Expand Down Expand Up @@ -92,10 +95,14 @@ void testStreamingWithRealAnthropicApi() throws InterruptedException {
// Add delay to avoid rapid requests
Thread.sleep(2000);

AnthropicApi anthropicApi =
AnthropicApi.builder().apiKey(System.getenv("ANTHROPIC_API_KEY")).build();
AnthropicChatModel anthropicModel =
AnthropicChatModel.builder().anthropicApi(anthropicApi).build();
var options =
AnthropicChatOptions.builder()
.model(CLAUDE_MODEL)
.maxTokens(1024)
.apiKey(System.getenv("ANTHROPIC_API_KEY"))
.build();

AnthropicChatModel anthropicModel = AnthropicChatModel.builder().options(options).build();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The builder method .options() is incorrect. It should be .withDefaultOptions() to comply with the updated Spring AI API and avoid a compilation error.

Suggested change
AnthropicChatModel anthropicModel = AnthropicChatModel.builder().options(options).build();
AnthropicChatModel anthropicModel = AnthropicChatModel.builder().withDefaultOptions(options).build();


SpringAI springAI = new SpringAI(anthropicModel, CLAUDE_MODEL);

Expand Down Expand Up @@ -134,10 +141,14 @@ void testStreamingWithRealAnthropicApi() throws InterruptedException {

@Test
void testAgentWithToolsAndRealApi() {
AnthropicApi anthropicApi =
AnthropicApi.builder().apiKey(System.getenv("ANTHROPIC_API_KEY")).build();
AnthropicChatModel anthropicModel =
AnthropicChatModel.builder().anthropicApi(anthropicApi).build();
var options =
AnthropicChatOptions.builder()
.model(CLAUDE_MODEL)
.maxTokens(1024)
.apiKey(System.getenv("ANTHROPIC_API_KEY"))
.build();

AnthropicChatModel anthropicModel = AnthropicChatModel.builder().options(options).build();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The builder method .options() is incorrect. It should be .withDefaultOptions() to comply with the updated Spring AI API and avoid a compilation error.

Suggested change
AnthropicChatModel anthropicModel = AnthropicChatModel.builder().options(options).build();
AnthropicChatModel anthropicModel = AnthropicChatModel.builder().withDefaultOptions(options).build();


LlmAgent agent =
LlmAgent.builder()
Expand Down Expand Up @@ -175,10 +186,13 @@ void testAgentWithToolsAndRealApi() {
@Test
void testDirectComparisonNonStreamingVsStreaming() throws InterruptedException {
// Test both non-streaming and streaming with the same model to compare behavior
AnthropicApi anthropicApi =
AnthropicApi.builder().apiKey(System.getenv("ANTHROPIC_API_KEY")).build();
AnthropicChatModel anthropicModel =
AnthropicChatModel.builder().anthropicApi(anthropicApi).build();
var options =
AnthropicChatOptions.builder()
.model(CLAUDE_MODEL)
.maxTokens(1024)
.apiKey(System.getenv("ANTHROPIC_API_KEY"))
.build();
AnthropicChatModel anthropicModel = AnthropicChatModel.builder().options(options).build();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The builder method .options() is incorrect. It should be .withDefaultOptions() to comply with the updated Spring AI API and avoid a compilation error.

Suggested change
AnthropicChatModel anthropicModel = AnthropicChatModel.builder().options(options).build();
AnthropicChatModel anthropicModel = AnthropicChatModel.builder().withDefaultOptions(options).build();


SpringAI springAI = new SpringAI(anthropicModel, CLAUDE_MODEL);

Expand Down Expand Up @@ -271,13 +285,13 @@ void testDirectComparisonNonStreamingVsStreaming() throws InterruptedException {
@Test
void testConfigurationOptions() {
// Test with custom configuration
AnthropicChatOptions options =
AnthropicChatOptions.builder().model(CLAUDE_MODEL).temperature(0.7).maxTokens(100).build();

AnthropicApi anthropicApi =
AnthropicApi.builder().apiKey(System.getenv("ANTHROPIC_API_KEY")).build();
AnthropicChatModel anthropicModel =
AnthropicChatModel.builder().anthropicApi(anthropicApi).defaultOptions(options).build();
var options =
AnthropicChatOptions.builder()
.model(CLAUDE_MODEL)
.maxTokens(1024)
.apiKey(System.getenv("ANTHROPIC_API_KEY"))
.build();
AnthropicChatModel anthropicModel = AnthropicChatModel.builder().options(options).build();
Comment on lines +288 to +294
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This test testConfigurationOptions seems to have lost its original purpose. It's supposed to test custom configurations like temperature and maxTokens, but the new code uses the same default options as other tests. Additionally, the builder method .options() is incorrect and should be .withDefaultOptions().

To fix this and restore the test's intent, you should reintroduce the custom options.

Suggested change
var options =
AnthropicChatOptions.builder()
.model(CLAUDE_MODEL)
.maxTokens(1024)
.apiKey(System.getenv("ANTHROPIC_API_KEY"))
.build();
AnthropicChatModel anthropicModel = AnthropicChatModel.builder().options(options).build();
var options =
AnthropicChatOptions.builder()
.model(CLAUDE_MODEL)
.temperature(0.7F)
.maxTokens(100)
.apiKey(System.getenv("ANTHROPIC_API_KEY"))
.build();
AnthropicChatModel anthropicModel =
AnthropicChatModel.builder().withDefaultOptions(options).build();


SpringAI springAI = new SpringAI(anthropicModel, CLAUDE_MODEL);

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
<otel.version>1.51.0</otel.version>
<mcp.version>0.17.2</mcp.version>
<errorprone.version>2.47.0</errorprone.version>
<google.genai.version>1.41.0</google.genai.version>
<google.genai.version>1.43.0</google.genai.version>
<protobuf.version>4.33.5</protobuf.version>
<junit.version>5.11.4</junit.version>
<mockito.version>5.20.0</mockito.version>
Expand Down