Rename checker block to conditional block
This commit is contained in:
parent
6207f9e50c
commit
330e7a6b62
|
|
@ -6,7 +6,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
|||
import it.cnr.isti.workflow.manager.blocks.configurations.annotations.LongText;
|
||||
import it.cnr.isti.workflow.manager.blocks.configurations.annotations.Structural;
|
||||
import it.cnr.isti.workflow.manager.blocks.configurations.annotations.UiDependency;
|
||||
import it.cnr.isti.workflow.manager.blocks.types.CheckerBlockType;
|
||||
import it.cnr.isti.workflow.manager.blocks.types.ConditionalBlockType;
|
||||
import it.cnr.isti.workflow.manager.llms.LLMDescriptor;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
|
|
@ -19,7 +19,7 @@ import lombok.NonNull;
|
|||
@NoArgsConstructor(access = lombok.AccessLevel.PROTECTED)
|
||||
@Getter
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CheckerBlockConfiguration extends BlockConfiguration<CheckerBlockType> {
|
||||
public class ConditionalBlockConfiguration extends BlockConfiguration<ConditionalBlockType> {
|
||||
|
||||
@UiDependency(field = "useLlm", equals = "false")
|
||||
@Structural
|
||||
|
|
@ -45,26 +45,36 @@ public class CheckerBlockConfiguration extends BlockConfiguration<CheckerBlockTy
|
|||
acceptVariableAsPlaceholder = true)
|
||||
String prompt;
|
||||
|
||||
@Structural
|
||||
@LongText(
|
||||
placeholder = "Define the output to propagate, for example: Approved request for ${{customer}}",
|
||||
tip = "The selected branch receives the rendered output template.",
|
||||
acceptVariableAsPlaceholder = true)
|
||||
@JsonProperty(required = true)
|
||||
String outputTemplate;
|
||||
|
||||
@Builder
|
||||
public CheckerBlockConfiguration(@NonNull String name, String condition,
|
||||
boolean useLlm, LLMDescriptor llmDescriptor, String prompt) {
|
||||
public ConditionalBlockConfiguration(@NonNull String name, String condition,
|
||||
boolean useLlm, LLMDescriptor llmDescriptor, String prompt, String outputTemplate) {
|
||||
super(name);
|
||||
this.condition = condition;
|
||||
this.useLlm = useLlm;
|
||||
this.llmDescriptor = llmDescriptor;
|
||||
this.prompt = prompt;
|
||||
this.outputTemplate = outputTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<CheckerBlockType> getBlockType() {
|
||||
return CheckerBlockType.class;
|
||||
public Class<ConditionalBlockType> getBlockType() {
|
||||
return ConditionalBlockType.class;
|
||||
}
|
||||
|
||||
public static CheckerBlockConfiguration empty() {
|
||||
CheckerBlockConfiguration configuration = new CheckerBlockConfiguration();
|
||||
configuration.name = CheckerBlockType.TYPE;
|
||||
public static ConditionalBlockConfiguration empty() {
|
||||
ConditionalBlockConfiguration configuration = new ConditionalBlockConfiguration();
|
||||
configuration.name = ConditionalBlockType.TYPE;
|
||||
configuration.condition = "";
|
||||
configuration.useLlm = false;
|
||||
configuration.outputTemplate = "";
|
||||
return configuration;
|
||||
}
|
||||
|
||||
|
|
@ -85,4 +95,10 @@ public class CheckerBlockConfiguration extends BlockConfiguration<CheckerBlockTy
|
|||
boolean isLlmPromptConfigurationValid() {
|
||||
return !useLlm || (prompt != null && !prompt.isBlank());
|
||||
}
|
||||
|
||||
@AssertTrue(message = "outputTemplate is required")
|
||||
@JsonIgnore
|
||||
boolean isOutputTemplateValid() {
|
||||
return outputTemplate != null && !outputTemplate.isBlank();
|
||||
}
|
||||
}
|
||||
|
|
@ -10,13 +10,13 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
import org.springframework.stereotype.Component;
|
||||
|
||||
import it.cnr.isti.workflow.manager.blocks.Block;
|
||||
import it.cnr.isti.workflow.manager.blocks.configurations.CheckerBlockConfiguration;
|
||||
import it.cnr.isti.workflow.manager.blocks.types.CheckerBlockType;
|
||||
import it.cnr.isti.workflow.manager.blocks.configurations.ConditionalBlockConfiguration;
|
||||
import it.cnr.isti.workflow.manager.blocks.types.ConditionalBlockType;
|
||||
import it.cnr.isti.workflow.manager.ios.IODescriptor;
|
||||
import it.cnr.isti.workflow.manager.ios.IOType;
|
||||
|
||||
@Component
|
||||
public class CheckerBlockFactory implements BlockFactory<CheckerBlockType, CheckerBlockConfiguration> {
|
||||
public class ConditionalBlockFactory implements BlockFactory<ConditionalBlockType, ConditionalBlockConfiguration> {
|
||||
|
||||
public static final String TRUE_OUTPUT = "true";
|
||||
public static final String FALSE_OUTPUT = "false";
|
||||
|
|
@ -24,15 +24,15 @@ public class CheckerBlockFactory implements BlockFactory<CheckerBlockType, Check
|
|||
private static final Pattern SPEL_VARIABLE_PATTERN = Pattern.compile("#([a-zA-Z_][a-zA-Z0-9_]*)");
|
||||
|
||||
@Autowired
|
||||
CheckerBlockType blockType;
|
||||
ConditionalBlockType blockType;
|
||||
|
||||
@Override
|
||||
public Block<CheckerBlockType> create(CheckerBlockConfiguration configuration) {
|
||||
public Block<ConditionalBlockType> create(ConditionalBlockConfiguration configuration) {
|
||||
List<IODescriptor> inputs = retrieveInputs(configuration).stream()
|
||||
.map(inputName -> IODescriptor.of(inputName, IOType.TEXT))
|
||||
.toList();
|
||||
|
||||
return Block.<CheckerBlockType>builder()
|
||||
return Block.<ConditionalBlockType>builder()
|
||||
.inputs(inputs)
|
||||
.output(IODescriptor.of(TRUE_OUTPUT, IOType.TEXT))
|
||||
.output(IODescriptor.of(FALSE_OUTPUT, IOType.TEXT))
|
||||
|
|
@ -42,16 +42,16 @@ public class CheckerBlockFactory implements BlockFactory<CheckerBlockType, Check
|
|||
}
|
||||
|
||||
@Override
|
||||
public Block<CheckerBlockType> createEmpty() {
|
||||
return create(CheckerBlockConfiguration.empty());
|
||||
public Block<ConditionalBlockType> createEmpty() {
|
||||
return create(ConditionalBlockConfiguration.empty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<CheckerBlockType> getBlockType() {
|
||||
return CheckerBlockType.class;
|
||||
public Class<ConditionalBlockType> getBlockType() {
|
||||
return ConditionalBlockType.class;
|
||||
}
|
||||
|
||||
private List<String> retrieveInputs(CheckerBlockConfiguration configuration) {
|
||||
private List<String> retrieveInputs(ConditionalBlockConfiguration configuration) {
|
||||
Set<String> inputNames = new LinkedHashSet<>();
|
||||
if (configuration.isUseLlm()) {
|
||||
collectMatches(inputNames, configuration.getPrompt(), PLACEHOLDER_PATTERN, 1);
|
||||
|
|
@ -59,6 +59,7 @@ public class CheckerBlockFactory implements BlockFactory<CheckerBlockType, Check
|
|||
collectMatches(inputNames, configuration.getCondition(), PLACEHOLDER_PATTERN, 1);
|
||||
collectMatches(inputNames, configuration.getCondition(), SPEL_VARIABLE_PATTERN, 1);
|
||||
}
|
||||
collectMatches(inputNames, configuration.getOutputTemplate(), PLACEHOLDER_PATTERN, 1);
|
||||
return List.copyOf(inputNames);
|
||||
}
|
||||
|
||||
|
|
@ -3,12 +3,12 @@ package it.cnr.isti.workflow.manager.blocks.types;
|
|||
import org.springframework.stereotype.Component;
|
||||
|
||||
import it.cnr.isti.workflow.manager.blocks.configurations.BlockConfiguration;
|
||||
import it.cnr.isti.workflow.manager.blocks.configurations.CheckerBlockConfiguration;
|
||||
import it.cnr.isti.workflow.manager.blocks.configurations.ConditionalBlockConfiguration;
|
||||
|
||||
@Component(CheckerBlockType.TYPE)
|
||||
public class CheckerBlockType implements BlockType {
|
||||
@Component(ConditionalBlockType.TYPE)
|
||||
public class ConditionalBlockType implements BlockType {
|
||||
|
||||
public static final String TYPE = "CheckerBlock";
|
||||
public static final String TYPE = "ConditionalBlock";
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
|
|
@ -32,6 +32,6 @@ public class CheckerBlockType implements BlockType {
|
|||
|
||||
@Override
|
||||
public Class<? extends BlockConfiguration<?>> getBlockConfigurationClass() {
|
||||
return CheckerBlockConfiguration.class;
|
||||
return ConditionalBlockConfiguration.class;
|
||||
}
|
||||
}
|
||||
|
|
@ -28,6 +28,8 @@ public class ExecutionObject {
|
|||
|
||||
ExecutionContext context;
|
||||
|
||||
List<Connection> stepConnections = new ArrayList<>();
|
||||
|
||||
long creationTime = System.currentTimeMillis();
|
||||
|
||||
String name;
|
||||
|
|
@ -38,6 +40,7 @@ public class ExecutionObject {
|
|||
@Builder
|
||||
public ExecutionObject(String executionName, FlowData flow) {
|
||||
this.name = executionName;
|
||||
this.stepConnections = flow.getConnections() == null ? List.of() : List.copyOf(flow.getConnections());
|
||||
|
||||
List<Step<?>> steps = getStepsFromFlow(flow);
|
||||
|
||||
|
|
|
|||
|
|
@ -17,20 +17,20 @@ import com.fasterxml.jackson.core.type.TypeReference;
|
|||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import it.cnr.isti.workflow.manager.blocks.Block;
|
||||
import it.cnr.isti.workflow.manager.blocks.configurations.CheckerBlockConfiguration;
|
||||
import it.cnr.isti.workflow.manager.blocks.factories.CheckerBlockFactory;
|
||||
import it.cnr.isti.workflow.manager.blocks.types.CheckerBlockType;
|
||||
import it.cnr.isti.workflow.manager.blocks.configurations.ConditionalBlockConfiguration;
|
||||
import it.cnr.isti.workflow.manager.blocks.factories.ConditionalBlockFactory;
|
||||
import it.cnr.isti.workflow.manager.blocks.types.ConditionalBlockType;
|
||||
import it.cnr.isti.workflow.manager.executions.steps.Input;
|
||||
import it.cnr.isti.workflow.manager.llms.LLMDescriptor;
|
||||
import it.cnr.isti.workflow.manager.llms.providers.LLMProvider;
|
||||
|
||||
@Component
|
||||
public class CheckerExecutor implements BlockExecutor<CheckerBlockType> {
|
||||
public class ConditionalExecutor implements BlockExecutor<ConditionalBlockType> {
|
||||
|
||||
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(CheckerExecutor.class);
|
||||
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(ConditionalExecutor.class);
|
||||
private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\$\\{\\{(.*?)}}");
|
||||
private static final String LLM_SYSTEM_PROMPT = """
|
||||
You are a workflow checker.
|
||||
You are a workflow conditional evaluator.
|
||||
Decide whether the provided prompt evaluates to true or false using the given inputs.
|
||||
Reply strictly as JSON in the form {"result":true} or {"result":false}.
|
||||
Do not add any extra text.
|
||||
|
|
@ -43,22 +43,22 @@ public class CheckerExecutor implements BlockExecutor<CheckerBlockType> {
|
|||
private Map<String, LLMProvider> llmProviders;
|
||||
|
||||
@Override
|
||||
public Map<String, Object> execute(Block<CheckerBlockType> block, List<Input> inputs,
|
||||
public Map<String, Object> execute(Block<ConditionalBlockType> block, List<Input> inputs,
|
||||
Map<String, Object> authorizations) {
|
||||
CheckerBlockConfiguration config = (CheckerBlockConfiguration) block.getSpecificConfiguration();
|
||||
ConditionalBlockConfiguration config = (ConditionalBlockConfiguration) block.getSpecificConfiguration();
|
||||
Map<String, Object> inputValues = collectInputValues(inputs);
|
||||
boolean matches = config.isUseLlm()
|
||||
? evaluateWithLlm(config, inputValues, authorizations)
|
||||
: evaluateWithExpression(config.getCondition(), inputValues);
|
||||
Object payload = buildPayload(inputValues);
|
||||
String payload = resolvePlaceholders(config.getOutputTemplate(), inputValues);
|
||||
return matches
|
||||
? Map.of(CheckerBlockFactory.TRUE_OUTPUT, payload)
|
||||
: Map.of(CheckerBlockFactory.FALSE_OUTPUT, payload);
|
||||
? Map.of(ConditionalBlockFactory.TRUE_OUTPUT, payload)
|
||||
: Map.of(ConditionalBlockFactory.FALSE_OUTPUT, payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<CheckerBlockType> getBlockType() {
|
||||
return CheckerBlockType.class;
|
||||
public Class<ConditionalBlockType> getBlockType() {
|
||||
return ConditionalBlockType.class;
|
||||
}
|
||||
|
||||
private Map<String, Object> collectInputValues(List<Input> inputs) {
|
||||
|
|
@ -69,13 +69,6 @@ public class CheckerExecutor implements BlockExecutor<CheckerBlockType> {
|
|||
return values;
|
||||
}
|
||||
|
||||
private Object buildPayload(Map<String, Object> inputValues) {
|
||||
if (inputValues.size() == 1) {
|
||||
return inputValues.values().iterator().next();
|
||||
}
|
||||
return inputValues;
|
||||
}
|
||||
|
||||
private boolean evaluateWithExpression(String expression, Map<String, Object> inputValues) {
|
||||
String normalizedExpression = normalizeExpression(expression);
|
||||
StandardEvaluationContext context = new StandardEvaluationContext(inputValues);
|
||||
|
|
@ -87,7 +80,7 @@ public class CheckerExecutor implements BlockExecutor<CheckerBlockType> {
|
|||
return result;
|
||||
}
|
||||
|
||||
private boolean evaluateWithLlm(CheckerBlockConfiguration config, Map<String, Object> inputValues,
|
||||
private boolean evaluateWithLlm(ConditionalBlockConfiguration config, Map<String, Object> inputValues,
|
||||
Map<String, Object> authorizations) {
|
||||
LLMDescriptor llmDescriptor = config.getLlmDescriptor();
|
||||
LLMProvider llmProvider = resolveProvider(llmDescriptor.provider());
|
||||
|
|
@ -115,7 +108,7 @@ public class CheckerExecutor implements BlockExecutor<CheckerBlockType> {
|
|||
.orElseThrow(() -> new IllegalArgumentException("Provider not found: " + providerName));
|
||||
}
|
||||
|
||||
private String buildLlmPrompt(CheckerBlockConfiguration config, Map<String, Object> inputValues) {
|
||||
private String buildLlmPrompt(ConditionalBlockConfiguration config, Map<String, Object> inputValues) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(LLM_SYSTEM_PROMPT).append("\n");
|
||||
builder.append("Decision prompt: ").append(resolvePlaceholders(config.getPrompt(), inputValues)).append("\n");
|
||||
|
|
@ -143,7 +136,7 @@ public class CheckerExecutor implements BlockExecutor<CheckerBlockType> {
|
|||
|
||||
private boolean parseBooleanResponse(String response) {
|
||||
if (!StringUtils.hasText(response)) {
|
||||
throw new IllegalArgumentException("Empty response from LLM checker");
|
||||
throw new IllegalArgumentException("Empty response from LLM conditional evaluator");
|
||||
}
|
||||
|
||||
String trimmed = response.trim();
|
||||
|
|
@ -170,9 +163,9 @@ public class CheckerExecutor implements BlockExecutor<CheckerBlockType> {
|
|||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.debug("Unable to parse checker LLM response as JSON: {}", trimmed, e);
|
||||
logger.debug("Unable to parse conditional LLM response as JSON: {}", trimmed, e);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Checker LLM response must be true, false, or JSON containing result");
|
||||
throw new IllegalArgumentException("Conditional LLM response must be true, false, or JSON containing result");
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue