diff --git a/langchain4j-spring-boot-starter/src/main/java/dev/langchain4j/service/spring/AiServiceScannerProcessor.java b/langchain4j-spring-boot-starter/src/main/java/dev/langchain4j/service/spring/AiServiceScannerProcessor.java index 62f4b622..a11995c6 100644 --- a/langchain4j-spring-boot-starter/src/main/java/dev/langchain4j/service/spring/AiServiceScannerProcessor.java +++ b/langchain4j-spring-boot-starter/src/main/java/dev/langchain4j/service/spring/AiServiceScannerProcessor.java @@ -5,8 +5,11 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.boot.autoconfigure.AutoConfigurationPackages; +import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; +import org.springframework.core.annotation.AnnotationUtils; import org.springframework.stereotype.Component; +import org.springframework.util.ClassUtils; import java.util.Collections; import java.util.LinkedHashSet; @@ -47,6 +50,15 @@ private Set getBasePackages(ConfigurableListableBeanFactory beanFactory) } } + String[] applicationBean = beanFactory.getBeanNamesForAnnotation(SpringBootApplication.class); + SpringBootApplication springbootApplication = AnnotationUtils.findAnnotation(beanFactory.getType(applicationBean[0]), SpringBootApplication.class); + if (springbootApplication != null) { + Collections.addAll(basePackages, springbootApplication.scanBasePackages()); + for (Class aClass : springbootApplication.scanBasePackageClasses()) { + basePackages.add(ClassUtils.getPackageName(aClass)); + } + } + return basePackages; } } diff --git a/langchain4j-spring-boot-starter/src/test/java/dev/langchain4j/service/spring/mode/automatic/scanPackages/aiService/ScanPackageAiService.java b/langchain4j-spring-boot-starter/src/test/java/dev/langchain4j/service/spring/mode/automatic/scanPackages/aiService/ScanPackageAiService.java new file mode 100644 index 00000000..e30b0189 --- /dev/null +++ b/langchain4j-spring-boot-starter/src/test/java/dev/langchain4j/service/spring/mode/automatic/scanPackages/aiService/ScanPackageAiService.java @@ -0,0 +1,9 @@ +package dev.langchain4j.service.spring.mode.automatic.scanPackages.aiService; + +import dev.langchain4j.service.spring.AiService; + +@AiService +public interface ScanPackageAiService { + + String chat(String userMessage); +} \ No newline at end of file diff --git a/langchain4j-spring-boot-starter/src/test/java/dev/langchain4j/service/spring/mode/automatic/scanPackages/package1/ScanPackageAiServiceApplication.java b/langchain4j-spring-boot-starter/src/test/java/dev/langchain4j/service/spring/mode/automatic/scanPackages/package1/ScanPackageAiServiceApplication.java new file mode 100644 index 00000000..63cdff94 --- /dev/null +++ b/langchain4j-spring-boot-starter/src/test/java/dev/langchain4j/service/spring/mode/automatic/scanPackages/package1/ScanPackageAiServiceApplication.java @@ -0,0 +1,12 @@ +package dev.langchain4j.service.spring.mode.automatic.scanPackages.package1; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication(scanBasePackages = {"dev.langchain4j.service.spring.mode.automatic.scanPackages.aiService"}) +public class ScanPackageAiServiceApplication { + + public static void main(String[] args) { + SpringApplication.run(ScanPackageAiServiceApplication.class, args); + } +} \ No newline at end of file diff --git a/langchain4j-spring-boot-starter/src/test/java/dev/langchain4j/service/spring/mode/automatic/scanPackages/package1/ScanPackageAiServiceIT.java b/langchain4j-spring-boot-starter/src/test/java/dev/langchain4j/service/spring/mode/automatic/scanPackages/package1/ScanPackageAiServiceIT.java new file mode 100644 index 00000000..dd7cb304 --- /dev/null +++ b/langchain4j-spring-boot-starter/src/test/java/dev/langchain4j/service/spring/mode/automatic/scanPackages/package1/ScanPackageAiServiceIT.java @@ -0,0 +1,40 @@ +package dev.langchain4j.service.spring.mode.automatic.scanPackages.package1; + +import dev.langchain4j.service.spring.AiServicesAutoConfig; +import dev.langchain4j.service.spring.mode.automatic.scanPackages.aiService.ScanPackageAiService; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +import static dev.langchain4j.service.spring.mode.ApiKeys.OPENAI_API_KEY; +import static org.assertj.core.api.Assertions.assertThat; + +class ScanPackageAiServiceIT { + + ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(AiServicesAutoConfig.class)); + + @Test + void should_create_AI_service_that_use_scanPackages_value() { + + contextRunner + .withPropertyValues( + "langchain4j.open-ai.chat-model.api-key=" + OPENAI_API_KEY, + "langchain4j.open-ai.chat-model.max-tokens=20", + "langchain4j.open-ai.chat-model.temperature=0.0" + ) + .withUserConfiguration(ScanPackageAiServiceApplication.class) + .run(context -> { + + // given + ScanPackageAiService aiService = context.getBean(ScanPackageAiService.class); + + // when + String answer = aiService.chat("What is the capital of Germany?"); + + // then + assertThat(answer).containsIgnoringCase("Berlin"); + }); + } + +} \ No newline at end of file diff --git a/langchain4j-spring-boot-starter/src/test/java/dev/langchain4j/service/spring/mode/automatic/scanPackages/package2/ScanPackageAiServiceApplication.java b/langchain4j-spring-boot-starter/src/test/java/dev/langchain4j/service/spring/mode/automatic/scanPackages/package2/ScanPackageAiServiceApplication.java new file mode 100644 index 00000000..cc30cd67 --- /dev/null +++ b/langchain4j-spring-boot-starter/src/test/java/dev/langchain4j/service/spring/mode/automatic/scanPackages/package2/ScanPackageAiServiceApplication.java @@ -0,0 +1,13 @@ +package dev.langchain4j.service.spring.mode.automatic.scanPackages.package2; + +import dev.langchain4j.service.spring.mode.automatic.scanPackages.aiService.ScanPackageAiService; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication(scanBasePackageClasses = ScanPackageAiService.class) +public class ScanPackageAiServiceApplication { + + public static void main(String[] args) { + SpringApplication.run(ScanPackageAiServiceApplication.class, args); + } +} \ No newline at end of file diff --git a/langchain4j-spring-boot-starter/src/test/java/dev/langchain4j/service/spring/mode/automatic/scanPackages/package2/ScanPackageAiServiceIT.java b/langchain4j-spring-boot-starter/src/test/java/dev/langchain4j/service/spring/mode/automatic/scanPackages/package2/ScanPackageAiServiceIT.java new file mode 100644 index 00000000..8b409892 --- /dev/null +++ b/langchain4j-spring-boot-starter/src/test/java/dev/langchain4j/service/spring/mode/automatic/scanPackages/package2/ScanPackageAiServiceIT.java @@ -0,0 +1,40 @@ +package dev.langchain4j.service.spring.mode.automatic.scanPackages.package2; + +import dev.langchain4j.service.spring.AiServicesAutoConfig; +import dev.langchain4j.service.spring.mode.automatic.scanPackages.aiService.ScanPackageAiService; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +import static dev.langchain4j.service.spring.mode.ApiKeys.OPENAI_API_KEY; +import static org.assertj.core.api.Assertions.assertThat; + +class ScanPackageAiServiceIT { + + ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(AiServicesAutoConfig.class)); + + @Test + void should_create_AI_service_that_use_scanPackageClass_value() { + + contextRunner + .withPropertyValues( + "langchain4j.open-ai.chat-model.api-key=" + OPENAI_API_KEY, + "langchain4j.open-ai.chat-model.max-tokens=20", + "langchain4j.open-ai.chat-model.temperature=0.0" + ) + .withUserConfiguration(ScanPackageAiServiceApplication.class) + .run(context -> { + + // given + ScanPackageAiService aiService = context.getBean(ScanPackageAiService.class); + + // when + String answer = aiService.chat("What is the capital of Germany?"); + + // then + assertThat(answer).containsIgnoringCase("Berlin"); + }); + } + +} \ No newline at end of file