diff --git a/langchain4j-anthropic-spring-boot-starter/pom.xml b/langchain4j-anthropic-spring-boot-starter/pom.xml new file mode 100644 index 00000000..9be3489a --- /dev/null +++ b/langchain4j-anthropic-spring-boot-starter/pom.xml @@ -0,0 +1,67 @@ + + + 4.0.0 + + + dev.langchain4j + langchain4j-spring + 0.29.0-SNAPSHOT + ../pom.xml + + + langchain4j-anthropic-spring-boot-starter + LangChain4j Spring Boot starter for Anthropic + + + + + dev.langchain4j + langchain4j-anthropic + ${project.version} + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-autoconfigure-processor + true + + + + + org.projectlombok + lombok + provided + + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + Apache-2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + A business-friendly OSS license + + + + \ No newline at end of file diff --git a/langchain4j-anthropic-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/AutoConfig.java b/langchain4j-anthropic-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/AutoConfig.java new file mode 100644 index 00000000..5348e335 --- /dev/null +++ b/langchain4j-anthropic-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/AutoConfig.java @@ -0,0 +1,56 @@ +package dev.langchain4j.anthropic.spring; + +import dev.langchain4j.model.anthropic.AnthropicChatModel; +import dev.langchain4j.model.anthropic.AnthropicStreamingChatModel; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + +import static dev.langchain4j.anthropic.spring.Properties.PREFIX; + +@AutoConfiguration +@EnableConfigurationProperties(Properties.class) +public class AutoConfig { + + @Bean + @ConditionalOnProperty(PREFIX + ".chat-model.api-key") + AnthropicChatModel anthropicChatModel(Properties properties) { + ChatModelProperties chatModelProperties = properties.getChatModel(); + return AnthropicChatModel.builder() + .baseUrl(chatModelProperties.getBaseUrl()) + .apiKey(chatModelProperties.getApiKey()) + .version(chatModelProperties.getVersion()) + .modelName(chatModelProperties.getModelName()) + .temperature(chatModelProperties.getTemperature()) + .topP(chatModelProperties.getTopP()) + .topK(chatModelProperties.getTopK()) + .maxTokens(chatModelProperties.getMaxTokens()) + .stopSequences(chatModelProperties.getStopSequences()) + .timeout(chatModelProperties.getTimeout()) + .maxRetries(chatModelProperties.getMaxRetries()) + .logRequests(chatModelProperties.getLogRequests()) + .logResponses(chatModelProperties.getLogResponses()) + .build(); + } + + @Bean + @ConditionalOnProperty(PREFIX + ".streaming-chat-model.api-key") + AnthropicStreamingChatModel anthropicStreamingChatModel(Properties properties) { + ChatModelProperties chatModelProperties = properties.getStreamingChatModel(); + return AnthropicStreamingChatModel.builder() + .baseUrl(chatModelProperties.getBaseUrl()) + .apiKey(chatModelProperties.getApiKey()) + .version(chatModelProperties.getVersion()) + .modelName(chatModelProperties.getModelName()) + .temperature(chatModelProperties.getTemperature()) + .topP(chatModelProperties.getTopP()) + .topK(chatModelProperties.getTopK()) + .maxTokens(chatModelProperties.getMaxTokens()) + .stopSequences(chatModelProperties.getStopSequences()) + .timeout(chatModelProperties.getTimeout()) + .logRequests(chatModelProperties.getLogRequests()) + .logResponses(chatModelProperties.getLogResponses()) + .build(); + } +} \ No newline at end of file diff --git a/langchain4j-anthropic-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/ChatModelProperties.java b/langchain4j-anthropic-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/ChatModelProperties.java new file mode 100644 index 00000000..c3c53b87 --- /dev/null +++ b/langchain4j-anthropic-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/ChatModelProperties.java @@ -0,0 +1,26 @@ +package dev.langchain4j.anthropic.spring; + +import lombok.Getter; +import lombok.Setter; + +import java.time.Duration; +import java.util.List; + +@Getter +@Setter +class ChatModelProperties { + + String baseUrl; + String apiKey; + String version; + String modelName; + Double temperature; + Double topP; + Integer topK; + Integer maxTokens; + List stopSequences; + Duration timeout; + Integer maxRetries; + Boolean logRequests; + Boolean logResponses; +} \ No newline at end of file diff --git a/langchain4j-anthropic-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/Properties.java b/langchain4j-anthropic-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/Properties.java new file mode 100644 index 00000000..8457629d --- /dev/null +++ b/langchain4j-anthropic-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/Properties.java @@ -0,0 +1,20 @@ +package dev.langchain4j.anthropic.spring; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.NestedConfigurationProperty; + +@Getter +@Setter +@ConfigurationProperties(prefix = Properties.PREFIX) +public class Properties { + + static final String PREFIX = "langchain4j.anthropic"; + + @NestedConfigurationProperty + ChatModelProperties chatModel; + + @NestedConfigurationProperty + ChatModelProperties streamingChatModel; +} diff --git a/langchain4j-anthropic-spring-boot-starter/src/main/resources/META-INF/spring.factories b/langchain4j-anthropic-spring-boot-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..6588156b --- /dev/null +++ b/langchain4j-anthropic-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=dev.langchain4j.anthropic.spring.AutoConfig \ No newline at end of file diff --git a/langchain4j-anthropic-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/langchain4j-anthropic-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..6fbc1082 --- /dev/null +++ b/langchain4j-anthropic-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +dev.langchain4j.anthropic.spring.AutoConfig \ No newline at end of file diff --git a/langchain4j-anthropic-spring-boot-starter/src/test/java/dev/langchain4j/anthropic/spring/AutoConfigIT.java b/langchain4j-anthropic-spring-boot-starter/src/test/java/dev/langchain4j/anthropic/spring/AutoConfigIT.java new file mode 100644 index 00000000..d150f7ae --- /dev/null +++ b/langchain4j-anthropic-spring-boot-starter/src/test/java/dev/langchain4j/anthropic/spring/AutoConfigIT.java @@ -0,0 +1,76 @@ +package dev.langchain4j.anthropic.spring; + +import dev.langchain4j.data.message.AiMessage; +import dev.langchain4j.model.StreamingResponseHandler; +import dev.langchain4j.model.anthropic.AnthropicChatModel; +import dev.langchain4j.model.anthropic.AnthropicStreamingChatModel; +import dev.langchain4j.model.chat.ChatLanguageModel; +import dev.langchain4j.model.chat.StreamingChatLanguageModel; +import dev.langchain4j.model.output.Response; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +import java.util.concurrent.CompletableFuture; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.assertj.core.api.Assertions.assertThat; + +class AutoConfigIT { + + private static final String API_KEY = System.getenv("ANTHROPIC_API_KEY"); + + ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(AutoConfig.class)); + + @Test + void should_provide_chat_model() { + contextRunner + .withPropertyValues( + "langchain4j.anthropic.chat-model.api-key=" + API_KEY, + "langchain4j.anthropic.chat-model.max-tokens=20" + ) + .run(context -> { + + ChatLanguageModel chatLanguageModel = context.getBean(ChatLanguageModel.class); + assertThat(chatLanguageModel).isInstanceOf(AnthropicChatModel.class); + assertThat(chatLanguageModel.generate("What is the capital of Germany?")).contains("Berlin"); + + assertThat(context.getBean(AnthropicChatModel.class)).isSameAs(chatLanguageModel); + }); + } + + @Test + void should_provide_streaming_chat_model() { + contextRunner + .withPropertyValues( + "langchain4j.anthropic.streaming-chat-model.api-key=" + API_KEY, + "langchain4j.anthropic.streaming-chat-model.max-tokens=20" + ) + .run(context -> { + + StreamingChatLanguageModel streamingChatLanguageModel = context.getBean(StreamingChatLanguageModel.class); + assertThat(streamingChatLanguageModel).isInstanceOf(AnthropicStreamingChatModel.class); + CompletableFuture> future = new CompletableFuture<>(); + streamingChatLanguageModel.generate("What is the capital of Germany?", new StreamingResponseHandler() { + + @Override + public void onNext(String token) { + } + + @Override + public void onComplete(Response response) { + future.complete(response); + } + + @Override + public void onError(Throwable error) { + } + }); + Response response = future.get(60, SECONDS); + assertThat(response.content().text()).contains("Berlin"); + + assertThat(context.getBean(AnthropicStreamingChatModel.class)).isSameAs(streamingChatLanguageModel); + }); + } +} \ No newline at end of file diff --git a/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/AutoConfig.java b/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/AutoConfig.java similarity index 99% rename from langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/AutoConfig.java rename to langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/AutoConfig.java index 8e47ecba..725fc08a 100644 --- a/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/AutoConfig.java +++ b/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/AutoConfig.java @@ -1,4 +1,4 @@ -package dev.langchain4j.openai.spring; +package dev.langchain4j.anthropic.spring; import dev.langchain4j.model.openai.*; import org.springframework.boot.autoconfigure.AutoConfiguration; diff --git a/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/ChatModelProperties.java b/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/ChatModelProperties.java similarity index 94% rename from langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/ChatModelProperties.java rename to langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/ChatModelProperties.java index 75e98ea7..b5e29b51 100644 --- a/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/ChatModelProperties.java +++ b/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/ChatModelProperties.java @@ -1,4 +1,4 @@ -package dev.langchain4j.openai.spring; +package dev.langchain4j.anthropic.spring; import lombok.Getter; import lombok.Setter; diff --git a/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/EmbeddingModelProperties.java b/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/EmbeddingModelProperties.java similarity index 92% rename from langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/EmbeddingModelProperties.java rename to langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/EmbeddingModelProperties.java index 1a227994..e499ab1d 100644 --- a/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/EmbeddingModelProperties.java +++ b/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/EmbeddingModelProperties.java @@ -1,4 +1,4 @@ -package dev.langchain4j.openai.spring; +package dev.langchain4j.anthropic.spring; import lombok.Getter; import lombok.Setter; diff --git a/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/ImageModelProperties.java b/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/ImageModelProperties.java similarity index 93% rename from langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/ImageModelProperties.java rename to langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/ImageModelProperties.java index bd96c7e0..5a01d2b1 100644 --- a/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/ImageModelProperties.java +++ b/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/ImageModelProperties.java @@ -1,4 +1,4 @@ -package dev.langchain4j.openai.spring; +package dev.langchain4j.anthropic.spring; import lombok.Getter; import lombok.Setter; diff --git a/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/LanguageModelProperties.java b/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/LanguageModelProperties.java similarity index 91% rename from langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/LanguageModelProperties.java rename to langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/LanguageModelProperties.java index 0101313b..994c9bbf 100644 --- a/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/LanguageModelProperties.java +++ b/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/LanguageModelProperties.java @@ -1,4 +1,4 @@ -package dev.langchain4j.openai.spring; +package dev.langchain4j.anthropic.spring; import lombok.Getter; import lombok.Setter; diff --git a/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/ModerationModelProperties.java b/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/ModerationModelProperties.java similarity index 91% rename from langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/ModerationModelProperties.java rename to langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/ModerationModelProperties.java index 80420ff1..e26aa282 100644 --- a/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/ModerationModelProperties.java +++ b/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/ModerationModelProperties.java @@ -1,4 +1,4 @@ -package dev.langchain4j.openai.spring; +package dev.langchain4j.anthropic.spring; import lombok.Getter; import lombok.Setter; diff --git a/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/Properties.java b/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/Properties.java similarity index 95% rename from langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/Properties.java rename to langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/Properties.java index 8cd88766..3a36498c 100644 --- a/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/Properties.java +++ b/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/Properties.java @@ -1,4 +1,4 @@ -package dev.langchain4j.openai.spring; +package dev.langchain4j.anthropic.spring; import lombok.Getter; import lombok.Setter; diff --git a/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/ProxyProperties.java b/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/ProxyProperties.java similarity index 90% rename from langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/ProxyProperties.java rename to langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/ProxyProperties.java index 141af329..aa58287f 100644 --- a/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/openai/spring/ProxyProperties.java +++ b/langchain4j-open-ai-spring-boot-starter/src/main/java/dev/langchain4j/anthropic/spring/ProxyProperties.java @@ -1,4 +1,4 @@ -package dev.langchain4j.openai.spring; +package dev.langchain4j.anthropic.spring; import lombok.Getter; import lombok.Setter; diff --git a/langchain4j-open-ai-spring-boot-starter/src/test/java/dev/langchain4j/openai/spring/AutoConfigIT.java b/langchain4j-open-ai-spring-boot-starter/src/test/java/dev/langchain4j/anthropic/spring/AutoConfigIT.java similarity index 99% rename from langchain4j-open-ai-spring-boot-starter/src/test/java/dev/langchain4j/openai/spring/AutoConfigIT.java rename to langchain4j-open-ai-spring-boot-starter/src/test/java/dev/langchain4j/anthropic/spring/AutoConfigIT.java index 3f99a713..a48d44d7 100644 --- a/langchain4j-open-ai-spring-boot-starter/src/test/java/dev/langchain4j/openai/spring/AutoConfigIT.java +++ b/langchain4j-open-ai-spring-boot-starter/src/test/java/dev/langchain4j/anthropic/spring/AutoConfigIT.java @@ -1,4 +1,4 @@ -package dev.langchain4j.openai.spring; +package dev.langchain4j.anthropic.spring; import dev.langchain4j.data.message.AiMessage; import dev.langchain4j.model.StreamingResponseHandler; diff --git a/pom.xml b/pom.xml index c0f38a58..8e85966d 100644 --- a/pom.xml +++ b/pom.xml @@ -14,6 +14,7 @@ https://github.com/langchain4j/langchain4j-spring + langchain4j-anthropic-spring-boot-starter langchain4j-ollama-spring-boot-starter langchain4j-open-ai-spring-boot-starter