Skip to content

Initial Support for the Anthropic Java SDK#5366

Closed
sobychacko wants to merge 1 commit intospring-projects:mainfrom
sobychacko:feature/spring-ai-anthropic-sdk
Closed

Initial Support for the Anthropic Java SDK#5366
sobychacko wants to merge 1 commit intospring-projects:mainfrom
sobychacko:feature/spring-ai-anthropic-sdk

Conversation

@sobychacko
Copy link
Contributor

@sobychacko sobychacko commented Jan 30, 2026

Initial support for the Anthropic Java SDK. Current support in the PR includes both sync and async chat model interactions with tool calling support in both cases.

@sobychacko sobychacko changed the title Initial Support for Anthropic Java SDK Initial Support for the Anthropic Java SDK Jan 30, 2026
Copy link
Member

@ericbottard ericbottard left a comment

Choose a reason for hiding this comment

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

Added some comments related to JSpecify and builder.
This does not constitue a thorough review of the anthropic aspects.

/**
* Maximum number of tokens to generate in the response.
*/
@Nullable private Integer maxTokens;
Copy link
Member

Choose a reason for hiding this comment

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

All of those JSpecify annotations are out of place.
Should be

Suggested change
@Nullable private Integer maxTokens;
private @Nullable Integer maxTokens;

*/
public static final class Builder {

private final AnthropicSdkChatOptions options = new AnthropicSdkChatOptions();
Copy link
Member

Choose a reason for hiding this comment

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

Builders should be re-usable. Hence, they should not act on an pre-created instance of options, otherwise if you call build(), then mutate the builder, then build() again, you actually get the same instance and the result of the first call is mutated while the user may not realise.

@NullMarked
package org.springframework.ai.anthropicsdk.metadata;

import org.jspecify.annotations.NullMarked;
Copy link
Member

Choose a reason for hiding this comment

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

This package is empty

*/
private Map<String, String> customHeaders = new HashMap<>();

@Nullable public String getBaseUrl() {
Copy link
Member

Choose a reason for hiding this comment

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

Similarly, all the method annotations are out of place.

Should be

Suggested change
@Nullable public String getBaseUrl() {
public @Nullable String getBaseUrl() {

/**
* Creates a new AnthropicSdkChatModel with default options.
*/
public AnthropicSdkChatModel() {
Copy link
Member

Choose a reason for hiding this comment

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

There should be only ONE constructor, which should be private.
Users construct instance via the builder.

@sobychacko
Copy link
Contributor Author

@ericbottard Thanks for the review. The initial focus was to get the basic functionality done and have feature parity with the regular anthropic module for sync and async calls. I will go through this review and address them soon. Thanks again!

@sobychacko
Copy link
Contributor Author

@ericbottard Addressed most of the comments and issues you raised. Could you take a look again? Thanks!

@sdeleuze
Copy link
Contributor

sdeleuze commented Feb 2, 2026

@sobychacko How should we proceed for this PR versus #5282 ? I guess we should decide which one is merged first.

Edit: #5282 has been merged so please let me know if you need my inputs for the rebase.

@sobychacko
Copy link
Contributor Author

@sdeleuze I used jackson2 in this PR. I think I will make it jackson 3 based.

@sobychacko
Copy link
Contributor Author

@sdeleuze I think it might be better to wait for the jackson 3 changes with the broader upgrade in the framework. What do you think?

@sobychacko sobychacko force-pushed the feature/spring-ai-anthropic-sdk branch from 6f7950e to c7ee377 Compare February 3, 2026 23:29
@sobychacko sobychacko force-pushed the feature/spring-ai-anthropic-sdk branch 2 times, most recently from 40cb8ca to f729e83 Compare February 23, 2026 23:42
@sobychacko sobychacko force-pushed the feature/spring-ai-anthropic-sdk branch 2 times, most recently from fa7601c to a81f788 Compare March 4, 2026 00:39
@sobychacko sobychacko force-pushed the feature/spring-ai-anthropic-sdk branch from a81f788 to 32c2346 Compare March 11, 2026 01:50
* @author Yanming Zhou
* @since 1.0.0
*/
@AutoConfiguration(after = { RestClientAutoConfiguration.class, WebClientAutoConfiguration.class,
Copy link
Member

Choose a reason for hiding this comment

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

This configuration only defines 2 beans, one of type AnthropicApi and one of type AnthropicChatModel (ChatModel).

None of those are defined in the configurations cited in after, so that attribute should be removed

ToolCallingAutoConfiguration.class, SpringAiRetryAutoConfiguration.class })
@EnableConfigurationProperties({ AnthropicChatProperties.class, AnthropicConnectionProperties.class })
@ConditionalOnClass(AnthropicApi.class)
@AutoConfiguration(after = { ToolCallingAutoConfiguration.class })
Copy link
Member

Choose a reason for hiding this comment

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

See my comment above about after

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The anthropicChatModel bean directly injects ToolCallingManager, which is provided by ToolCallingAutoConfiguration. Removing the after attribute breaks various auto-config tests since SpringAiTestAutoConfigurations.of() rely on it to include dependency configs in the test context. OpenAiSdkChatAutoConfiguration also follows the same pattern. Therefore I am keeping the after as is, unless you see further issues with that.

Copy link
Member

Choose a reason for hiding this comment

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

This is not correct. SpringAiTestAutoConfigurations is wrong too (it adding any config that is in after to the list of configs to load is wrong and does not reflect what happens at runtime). Please remove the afters from this PR and I'll also create a PR to get rid of SpringAiTestAutoConfigurations for M3

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ericbottard Based on our discussion on this matter, we are good here, right?

Copy link
Member

Choose a reason for hiding this comment

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

yes, let's postpone

@@ -0,0 +1,108 @@
/*
Copy link
Member

Choose a reason for hiding this comment

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

What's with that "legacy" module?

/**
* Builder for creating {@link AnthropicChatOptions} instances.
*/
public static final class Builder {
Copy link
Member

Choose a reason for hiding this comment

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

Please use the builder pattern found eg in 88fbe6b

*/
class AnthropicChatOptionsTests<B extends AnthropicChatOptions.Builder<B>>
extends AbstractChatOptionsTests<AnthropicChatOptions, B> {
class AnthropicChatOptionsTests {
Copy link
Member

Choose a reason for hiding this comment

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

See 88fbe6b for added builder tests

@sobychacko sobychacko force-pushed the feature/spring-ai-anthropic-sdk branch 2 times, most recently from 32dc776 to 8462ca6 Compare March 12, 2026 15:46
@sobychacko sobychacko added this to the 2.0.0-M3 milestone Mar 12, 2026
@sobychacko sobychacko marked this pull request as ready for review March 12, 2026 16:01
Rewrite the Anthropic integration from the ground up on the official
Anthropic Java SDK (v2.16.1), replacing the hand-rolled RestClient
implementation.

Core capabilities:

  - Sync and streaming chat with typed SDK response objects
  - Tool calling with JsonValue visitor pattern for schema conversion
  - Streaming tool calling with partial JSON accumulation and recursive
    multi-turn execution
  - Multi-modal inputs (base64 images, HTTPS URLs, PDF documents)
  - Extended thinking (ThinkingBlock, RedactedThinkingBlock) with
    budget control and adaptive mode
  - Citations across plain text, PDF, and custom content documents
    with three location types (char, page, content block)
  - Citation consistency validation on build to reject mixed settings
  - Prompt caching with 5 strategies, configurable TTL, content length
    thresholds, and multi-block system message support
  - Structured output via OutputConfig with JSON schema enforcement
    and effort control (LOW/MEDIUM/HIGH/MAX)
  - Per-request HTTP headers for beta features and request tracking
  - Claude Skills (XLSX, PPTX, DOCX, PDF) with Files API for
    downloading generated documents
  - disableParallelToolUse wiring into ToolChoice subtypes
  - Expose underlying SDK clients via getAnthropicClient() and
    getAnthropicClientAsync() for direct access to SDK features

Infrastructure:

  - Builder aligned with DefaultToolCallingChatOptions.Builder using
    self-type generics, mutate()/combineWith() pattern
  - SDK-native retry (no Spring RetryTemplate), matching spring-ai-openai-sdk
  - Raw SDK Message stored in metadata under "anthropic-response" key
  - Observation/tracing support with Micrometer for both sync and
    streaming paths
  - Default model: claude-haiku-4-5, default max tokens: 4096
  - Auto-configuration and starter modules
  - Comprehensive unit tests (55+) and integration tests (35+)
  - Reference documentation with configuration properties

Signed-off-by: Soby Chacko <soby.chacko@broadcom.com>
@sobychacko sobychacko force-pushed the feature/spring-ai-anthropic-sdk branch from 8d429ed to 8952059 Compare March 13, 2026 15:41
@tzolov tzolov self-assigned this Mar 13, 2026
@tzolov
Copy link
Contributor

tzolov commented Mar 13, 2026

Great contribution. Thanks @sobychacko
Rebased and merged at b0ce25a

@tzolov tzolov closed this Mar 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants