diff --git a/src/main/java/it/cnr/isti/workflow/manager/auth/repo/LoginEntity.java b/src/main/java/it/cnr/isti/workflow/manager/auth/repo/LoginEntity.java index 8754ad2..3429d65 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/auth/repo/LoginEntity.java +++ b/src/main/java/it/cnr/isti/workflow/manager/auth/repo/LoginEntity.java @@ -1,4 +1,4 @@ -package it.cnr.isti.workflow.manager.auth.model; +package it.cnr.isti.workflow.manager.auth.repo; import jakarta.persistence.Entity; import jakarta.persistence.Id; diff --git a/src/main/java/it/cnr/isti/workflow/manager/blocks/Block.java b/src/main/java/it/cnr/isti/workflow/manager/blocks/Block.java index ce63348..0c187f0 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/blocks/Block.java +++ b/src/main/java/it/cnr/isti/workflow/manager/blocks/Block.java @@ -14,12 +14,12 @@ import lombok.Singular; @NoArgsConstructor(access = lombok.AccessLevel.PROTECTED) @Getter -public class Block> { +public class Block { final String id = UUID.randomUUID().toString(); @Builder - public Block(C specificConfiguration, String name, T type, @Singular List inputs, @Singular List outputs, @Singular List bricks) { + public Block(BlockConfiguration specificConfiguration, String name, T type, @Singular List inputs, @Singular List outputs, @Singular List bricks) { this.specificConfiguration = specificConfiguration; this.name = name; this.type = type; @@ -35,6 +35,6 @@ public class Block> { List outputs; - C specificConfiguration; + BlockConfiguration specificConfiguration; } diff --git a/src/main/java/it/cnr/isti/workflow/manager/blocks/factories/BlockFactory.java b/src/main/java/it/cnr/isti/workflow/manager/blocks/factories/BlockFactory.java index 972d5bb..4dbbd2c 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/blocks/factories/BlockFactory.java +++ b/src/main/java/it/cnr/isti/workflow/manager/blocks/factories/BlockFactory.java @@ -13,7 +13,7 @@ public interface BlockFactory create(@Valid C configuration); + Block create(@Valid C configuration); /** * Returns the type of block this factory creates. diff --git a/src/main/java/it/cnr/isti/workflow/manager/blocks/factories/LLMBlockFactory.java b/src/main/java/it/cnr/isti/workflow/manager/blocks/factories/LLMBlockFactory.java index 57a30df..ad08279 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/blocks/factories/LLMBlockFactory.java +++ b/src/main/java/it/cnr/isti/workflow/manager/blocks/factories/LLMBlockFactory.java @@ -4,13 +4,12 @@ import java.util.HashSet; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; - 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.LLMBlockConfiguration; import it.cnr.isti.workflow.manager.blocks.types.LLMBlockType; +import jakarta.validation.Valid; @Component public class LLMBlockFactory implements BlockFactory { @@ -19,16 +18,7 @@ public class LLMBlockFactory implements BlockFactory create(LLMBlockConfiguration configuration) { - String prompt =configuration.getPrompt(); - Block block = Block.builder() - .type(blockType).inputs(retrieveInputs(prompt)).output("response") - .specificConfiguration(configuration) - .build(); - - return block; - } + @Override public Class getBlockType() { @@ -46,4 +36,15 @@ public class LLMBlockFactory implements BlockFactory create(@Valid LLMBlockConfiguration configuration) { + String prompt =configuration.getPrompt(); + Block block = Block.builder() + .type(blockType).inputs(retrieveInputs(prompt)).output("response") + .specificConfiguration(configuration) + .build(); + + return block; } + + } diff --git a/src/main/java/it/cnr/isti/workflow/manager/bricks/LLMBrick.java b/src/main/java/it/cnr/isti/workflow/manager/bricks/LLMBrick.java index 2c6dd84..c2f4c1c 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/bricks/LLMBrick.java +++ b/src/main/java/it/cnr/isti/workflow/manager/bricks/LLMBrick.java @@ -1,20 +1,30 @@ package it.cnr.isti.workflow.manager.bricks; +import java.util.Map; -public class LLMBrick extends Brick { +import com.fasterxml.jackson.annotation.JsonIgnore; +import it.cnr.isti.workflow.manager.llms.providers.LLMProvider; - private final String provider; +public class LLMBrick extends Brick { + + @JsonIgnore + private final LLMProvider provider; + private final String model; + public String generate(String prompt, Map executionParameters) { + return provider.generate(model, prompt); + } - public LLMBrick(String provider, String model, String brickManager) { - super(provider + "_" + model, brickManager); + + public LLMBrick(LLMProvider provider, String model, String brickManager) { + super(provider.getName() + "_" + model, brickManager); this.provider = provider; this.model = model; } public String getProvider() { - return provider; + return provider.getName(); } public String getModel() { diff --git a/src/main/java/it/cnr/isti/workflow/manager/bricks/LLMBrickManager.java b/src/main/java/it/cnr/isti/workflow/manager/bricks/LLMBrickManager.java index 0a244b9..9f8107e 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/bricks/LLMBrickManager.java +++ b/src/main/java/it/cnr/isti/workflow/manager/bricks/LLMBrickManager.java @@ -21,7 +21,7 @@ public class LLMBrickManager extends BrickManager { public List getBricks() { List bricks = new ArrayList<>(); llmProviders.forEach(p -> p.getRegisteredModels().stream() - .map(model -> new LLMBrick(p.getClass().getSimpleName(), model, "llmBrickProvider")) + .map(model -> new LLMBrick(p, model, "llmBrickProvider")) .forEach(brick -> bricks.add(brick))); return bricks; } diff --git a/src/main/java/it/cnr/isti/workflow/manager/bricks/extensions/ExecutionBrick.java b/src/main/java/it/cnr/isti/workflow/manager/bricks/extensions/ExecutionBrick.java new file mode 100644 index 0000000..90fcaaa --- /dev/null +++ b/src/main/java/it/cnr/isti/workflow/manager/bricks/extensions/ExecutionBrick.java @@ -0,0 +1,8 @@ +package it.cnr.isti.workflow.manager.bricks.extensions; + +import java.util.Map; + +public interface ExecutionBrick { + + OUTPUT execute(Map parameters, Map configParameters); +} diff --git a/src/main/java/it/cnr/isti/workflow/manager/controllers/BlocksController.java b/src/main/java/it/cnr/isti/workflow/manager/controllers/BlocksController.java index 6aa2294..f04d28b 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/controllers/BlocksController.java +++ b/src/main/java/it/cnr/isti/workflow/manager/controllers/BlocksController.java @@ -51,12 +51,12 @@ public class BlocksController { @SuppressWarnings("unchecked") @PostMapping - public > Block create(@RequestBody @Valid C blockConfiguration) { + public > Block create(@RequestBody @Valid C blockConfiguration) { BlockFactory factory = (BlockFactory) blockFactories.stream() .filter(f -> f.getBlockType().equals(blockConfiguration.getType())) .findFirst() .orElseThrow(() -> new IllegalArgumentException("Block factory not found for type: " + blockConfiguration.getType())); - Block block = factory.create(blockConfiguration); + Block block = factory.create(blockConfiguration); return block; } diff --git a/src/main/java/it/cnr/isti/workflow/manager/controllers/FlowController.java b/src/main/java/it/cnr/isti/workflow/manager/controllers/FlowController.java index 3aeaa22..45079c7 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/controllers/FlowController.java +++ b/src/main/java/it/cnr/isti/workflow/manager/controllers/FlowController.java @@ -1,21 +1,32 @@ package it.cnr.isti.workflow.manager.controllers; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import it.cnr.isti.workflow.manager.auth.repo.LoginEntity; +import it.cnr.isti.workflow.manager.flows.FlowService; +import it.cnr.isti.workflow.manager.flows.model.Flow; import it.cnr.isti.workflow.manager.flows.repo.FlowEntity; @RestController @RequestMapping("/flows") public class FlowController { + @Autowired + FlowService flowService; + + @PostMapping - public ResponseEntity createFlow(@RequestBody FlowEntity flowEntity) { - FlowEntity createdFlow = flowService.createFlow(flowEntity); - + public ResponseEntity createFlow(@RequestBody Flow flow, @AuthenticationPrincipal + LoginEntity userDetails) { + FlowEntity createdFlow = flowService.createFlow(userDetails.getUsername(), flow); + return ResponseEntity.ok(createdFlow); } } diff --git a/src/main/java/it/cnr/isti/workflow/manager/executions/ExecutionContext.java b/src/main/java/it/cnr/isti/workflow/manager/executions/ExecutionContext.java new file mode 100644 index 0000000..96d5ac8 --- /dev/null +++ b/src/main/java/it/cnr/isti/workflow/manager/executions/ExecutionContext.java @@ -0,0 +1,100 @@ +package it.cnr.isti.workflow.manager.executions; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter() +@NoArgsConstructor +public class ExecutionContext { + + public enum Status { + CREATED(true, false, false), + INITIALIZING(true, false, false), + READY(true, false, false), + RUNNING(false, false, false), + WAITING(false, false, true), + SUCCESS(false, true, false), + ERROR(false, true, false); + + private boolean initState; + + private boolean finalState; + private boolean waitingState; + + Status(boolean initState, boolean finalState, boolean waitingState) { + this.initState = initState; + this.finalState = finalState; + this.waitingState = waitingState; + } + + public boolean isInitState() { + return initState; + } + + public boolean isFinalState() { + return finalState; + } + + public boolean isWaitingState() { + return waitingState ; + } + + public boolean isRunningState() { + return !finalState && !initState && !waitingState; + } + } + + Map inputs = new HashMap<>(); + + @Getter() + private Map result = new HashMap<>(); + + Long startTime = null; + Long endTime = null; + + List stepsUnderExecution = new ArrayList<>(); + List waitingSteps = new ArrayList<>(); + Map errors = new HashMap<>(); + List warnings = new ArrayList<>(); + + + Map> nodeResult = new HashMap<>(); + + Status status = Status.CREATED; + + public void setStatus(Status status) { + this.status = status; + if (status == Status.RUNNING) { + this.startTime = System.currentTimeMillis(); + } else if (status == Status.SUCCESS || status == Status.ERROR) { + this.endTime = System.currentTimeMillis(); + } + } + + public void addNodeResult(String nodeId, Map result) { + this.nodeResult.put(nodeId, result); + } + + public void addResult(String nodeId, String key, Object value) { + this.result.put(new FieldKey(nodeId, key), value); + } + + public void addInput(String key, Object value) { + this.inputs.put(key, value); + } + + public void addError(String nodeId, String error) { + this.errors.put(nodeId, error); + } + + public Map getExecutionResult() { + return Collections.unmodifiableMap(this.result); + } +} + diff --git a/src/main/java/it/cnr/isti/workflow/manager/executions/ExecutorDescriptor.java b/src/main/java/it/cnr/isti/workflow/manager/executions/ExecutorDescriptor.java new file mode 100644 index 0000000..d02dd5a --- /dev/null +++ b/src/main/java/it/cnr/isti/workflow/manager/executions/ExecutorDescriptor.java @@ -0,0 +1,28 @@ +package it.cnr.isti.workflow.manager.executions; + +import java.util.List; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NonNull; +import lombok.Singular; + +@Data +@AllArgsConstructor +@Builder +public class ExecutorDescriptor { + + + @NonNull + String identifier; + @NonNull + String name; + + String description; + @Singular + List inputNames; + @Singular + List outputNames; + +} diff --git a/src/main/java/it/cnr/isti/workflow/manager/executions/FieldKey.java b/src/main/java/it/cnr/isti/workflow/manager/executions/FieldKey.java new file mode 100644 index 0000000..160e87f --- /dev/null +++ b/src/main/java/it/cnr/isti/workflow/manager/executions/FieldKey.java @@ -0,0 +1,8 @@ +package it.cnr.isti.workflow.manager.executions; + +public record FieldKey(String nodeId, String fieldId) { + @Override + public final String toString() { + return nodeId + ":" + fieldId; + } +} diff --git a/src/main/java/it/cnr/isti/workflow/manager/executions/InputConsumer.java b/src/main/java/it/cnr/isti/workflow/manager/executions/InputConsumer.java new file mode 100644 index 0000000..d476e7e --- /dev/null +++ b/src/main/java/it/cnr/isti/workflow/manager/executions/InputConsumer.java @@ -0,0 +1,7 @@ +package it.cnr.isti.workflow.manager.executions; + +public interface InputConsumer { + + void consumeInput(String key, Object value); + +} diff --git a/src/main/java/it/cnr/isti/workflow/manager/executions/OutputProducer.java b/src/main/java/it/cnr/isti/workflow/manager/executions/OutputProducer.java new file mode 100644 index 0000000..558e44b --- /dev/null +++ b/src/main/java/it/cnr/isti/workflow/manager/executions/OutputProducer.java @@ -0,0 +1,9 @@ +package it.cnr.isti.workflow.manager.executions; + +public interface OutputProducer { + + void produceOutput(String key, Object value); + + void registerConsumer(InputConsumer consumer ); + +} diff --git a/src/main/java/it/cnr/isti/workflow/manager/executions/steps/Input.java b/src/main/java/it/cnr/isti/workflow/manager/executions/steps/Input.java new file mode 100644 index 0000000..06e4f8a --- /dev/null +++ b/src/main/java/it/cnr/isti/workflow/manager/executions/steps/Input.java @@ -0,0 +1,25 @@ +package it.cnr.isti.workflow.manager.executions.steps; + +import lombok.Getter; + +@Getter +public class Input { + + String name; + private Object value = null; + + private boolean registered = false; + + Input(String name) { + this.name = name; + } + + public void setValue(Object value) { + this.value = value; + } + + protected void registered() { + this.registered = true; + } + +} diff --git a/src/main/java/it/cnr/isti/workflow/manager/executions/steps/LLMStep.java b/src/main/java/it/cnr/isti/workflow/manager/executions/steps/LLMStep.java new file mode 100644 index 0000000..38ba74d --- /dev/null +++ b/src/main/java/it/cnr/isti/workflow/manager/executions/steps/LLMStep.java @@ -0,0 +1,15 @@ +package it.cnr.isti.workflow.manager.executions.steps; + +import it.cnr.isti.workflow.manager.blocks.Block; +import it.cnr.isti.workflow.manager.blocks.types.LLMBlockType; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = lombok.AccessLevel.PRIVATE) +public class LLMStep extends Step { + + LLMStep(Block block) { + super(block); + } + + +} diff --git a/src/main/java/it/cnr/isti/workflow/manager/executions/steps/Output.java b/src/main/java/it/cnr/isti/workflow/manager/executions/steps/Output.java new file mode 100644 index 0000000..c3d4f40 --- /dev/null +++ b/src/main/java/it/cnr/isti/workflow/manager/executions/steps/Output.java @@ -0,0 +1,29 @@ +package it.cnr.isti.workflow.manager.executions.steps; + +import java.util.List; + +import lombok.Getter; + +public class Output { + + @Getter + String name; + + private List consumers = List.of(); + + Output(String name) { + this.name = name; + } + + void register(Input input) { + this.consumers.add(input); + input.registered(); + } + + void setValue(Object value) { + for (Input consumer : consumers) { + consumer.setValue(value); + } + } + +} diff --git a/src/main/java/it/cnr/isti/workflow/manager/executions/steps/Step.java b/src/main/java/it/cnr/isti/workflow/manager/executions/steps/Step.java new file mode 100644 index 0000000..ab52ebb --- /dev/null +++ b/src/main/java/it/cnr/isti/workflow/manager/executions/steps/Step.java @@ -0,0 +1,32 @@ +package it.cnr.isti.workflow.manager.executions.steps; + +import java.util.List; + +import it.cnr.isti.workflow.manager.blocks.Block; +import it.cnr.isti.workflow.manager.blocks.types.BlockType; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = lombok.AccessLevel.PROTECTED) +@Getter +public abstract class Step { + + private Block block; + + List inputs = List.of(); + List outputs = List.of(); + + protected Step(Block block) { + // Initialize the step with the provided block + this.block = block; + this.block.getOutputs().forEach(outputName -> { + Output output = new Output(outputName); + this.outputs.add(output); + }); + this.block.getInputs().forEach(inputName -> { + Input input = new Input(inputName); + this.inputs.add(input); + }); + } + +} diff --git a/src/main/java/it/cnr/isti/workflow/manager/flows/FlowConverter.java b/src/main/java/it/cnr/isti/workflow/manager/flows/FlowConverter.java new file mode 100644 index 0000000..7b3c3d0 --- /dev/null +++ b/src/main/java/it/cnr/isti/workflow/manager/flows/FlowConverter.java @@ -0,0 +1,37 @@ +package it.cnr.isti.workflow.manager.flows; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import it.cnr.isti.workflow.manager.flows.model.Flow; +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; + +@Converter(autoApply = false) +public class FlowConverter implements AttributeConverter { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public String convertToDatabaseColumn(Flow flow) { + if (flow == null) { + return null; + } + try { + return objectMapper.writeValueAsString(flow); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("Errore nella serializzazione di Flow in JSON", e); + } + } + + @Override + public Flow convertToEntityAttribute(String dbData) { + if (dbData == null || dbData.isBlank()) { + return null; + } + try { + return objectMapper.readValue(dbData, Flow.class); + } catch (Exception e) { + throw new IllegalArgumentException("Errore nella deserializzazione di JSON in Flow", e); + } + } +} \ No newline at end of file diff --git a/src/main/java/it/cnr/isti/workflow/manager/flows/FlowService.java b/src/main/java/it/cnr/isti/workflow/manager/flows/FlowService.java index 51ee1c4..615038f 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/flows/FlowService.java +++ b/src/main/java/it/cnr/isti/workflow/manager/flows/FlowService.java @@ -15,15 +15,13 @@ public class FlowService { @Autowired FlowRepository flowRepository; - public FlowEntity createFlow(String name, String description, String owner, Flow flow) { + public FlowEntity createFlow(String owner, Flow flow) { FlowEntity flowEntity = new FlowEntity(); LocalDateTime now = LocalDateTime.now(); flowEntity.setCreatedAt(now); flowEntity.setLastUpdateAt(now); flowEntity.setFlow(flow); flowEntity.setOwner(owner); - flowEntity.setName(name); - flowEntity.setDescription(description); return flowRepository.save(flowEntity); } } diff --git a/src/main/java/it/cnr/isti/workflow/manager/flows/model/Connection.java b/src/main/java/it/cnr/isti/workflow/manager/flows/model/Connection.java index d86e0fc..7a20d03 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/flows/model/Connection.java +++ b/src/main/java/it/cnr/isti/workflow/manager/flows/model/Connection.java @@ -1,4 +1,4 @@ -package it.cnr.isti.workflow.manager.flows; +package it.cnr.isti.workflow.manager.flows.model; import java.util.UUID; diff --git a/src/main/java/it/cnr/isti/workflow/manager/flows/model/Flow.java b/src/main/java/it/cnr/isti/workflow/manager/flows/model/Flow.java index 4cfe4f1..e285ae7 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/flows/model/Flow.java +++ b/src/main/java/it/cnr/isti/workflow/manager/flows/model/Flow.java @@ -3,10 +3,12 @@ package it.cnr.isti.workflow.manager.flows.model; import java.util.List; import it.cnr.isti.workflow.manager.blocks.Block; +import jakarta.validation.constraints.NotBlank; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import lombok.NonNull; import lombok.Singular; @Data @@ -15,8 +17,14 @@ import lombok.Singular; @AllArgsConstructor public class Flow { + @NonNull + @NotBlank + String name; + + String description; + @Singular - List> blocks; + List> blocks; @Singular List connections; diff --git a/src/main/java/it/cnr/isti/workflow/manager/flows/repo/FlowEntity.java b/src/main/java/it/cnr/isti/workflow/manager/flows/repo/FlowEntity.java index 67f6446..07c3962 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/flows/repo/FlowEntity.java +++ b/src/main/java/it/cnr/isti/workflow/manager/flows/repo/FlowEntity.java @@ -1,10 +1,11 @@ package it.cnr.isti.workflow.manager.flows.repo; -import java.sql.Date; import java.time.LocalDateTime; +import it.cnr.isti.workflow.manager.flows.FlowConverter; import it.cnr.isti.workflow.manager.flows.model.Flow; import jakarta.persistence.Column; +import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; @@ -51,5 +52,6 @@ public class FlowEntity { @Lob @Column(name = "flow_data", columnDefinition = "TEXT") + @Convert(converter = FlowConverter.class) private Flow flow; } diff --git a/src/main/java/it/cnr/isti/workflow/manager/llms/providers/LLMProvider.java b/src/main/java/it/cnr/isti/workflow/manager/llms/providers/LLMProvider.java index 79b4659..1cb5db6 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/llms/providers/LLMProvider.java +++ b/src/main/java/it/cnr/isti/workflow/manager/llms/providers/LLMProvider.java @@ -4,6 +4,7 @@ import java.util.List; public interface LLMProvider { + String getName(); List getRegisteredModels(); String generate(String model, String prompt); diff --git a/src/main/java/it/cnr/isti/workflow/manager/llms/providers/google/GeminiLLMProvider.java b/src/main/java/it/cnr/isti/workflow/manager/llms/providers/google/GeminiLLMProvider.java index dda487d..a26dcb0 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/llms/providers/google/GeminiLLMProvider.java +++ b/src/main/java/it/cnr/isti/workflow/manager/llms/providers/google/GeminiLLMProvider.java @@ -25,14 +25,17 @@ public class GeminiLLMProvider implements LLMProvider { "gemini-1.5-flash", "gemini-1.5-pro", "gemini-2.0-flash", - "gemini-2.0-flash-lite" - ); - + "gemini-2.0-flash-lite"); public GeminiLLMProvider(WebClient.Builder webClientBuilder) { this.webClientBuilder = webClientBuilder; } + @Override + public String getName() { + return "GeminiLLMProvider"; + } + public void setApiKey(String apiKey) { this.apiKey = apiKey; } @@ -40,9 +43,9 @@ public class GeminiLLMProvider implements LLMProvider { public String generate(String model, String prompt) { Objects.requireNonNull(prompt, "prompt cannot be null"); Objects.requireNonNull(model, "model cannot be null"); - if (!MODELS.contains(model)) + if (!MODELS.contains(model)) throw new IllegalArgumentException("Model not supported: " + model); - + Map requestBody = Map.of( "contents", List.of( // Usa List.of invece di new Object[] Map.of( @@ -68,7 +71,7 @@ public class GeminiLLMProvider implements LLMProvider { .retryWhen( Retry.backoff(10, Duration.ofSeconds(30)) .filter(throwable -> throwable instanceof RuntimeException - || throwable instanceof java.util.concurrent.TimeoutException) + || throwable instanceof java.util.concurrent.TimeoutException) .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> new RuntimeException( "Error contacting Gemini API", retrySignal.failure()))); diff --git a/src/main/java/it/cnr/isti/workflow/manager/llms/providers/ollama/InternalOllamaLLMProvider.java b/src/main/java/it/cnr/isti/workflow/manager/llms/providers/ollama/InternalOllamaLLMProvider.java index 5086e9d..613c0a0 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/llms/providers/ollama/InternalOllamaLLMProvider.java +++ b/src/main/java/it/cnr/isti/workflow/manager/llms/providers/ollama/InternalOllamaLLMProvider.java @@ -1,7 +1,5 @@ package it.cnr.isti.workflow.manager.llms.providers.ollama; - - import java.time.Duration; import java.util.List; import java.util.Map; @@ -23,82 +21,88 @@ import reactor.core.publisher.Mono; @Service public class InternalOllamaLLMProvider implements LLMProvider { - private static final Logger log = org.slf4j.LoggerFactory.getLogger(InternalOllamaLLMProvider.class); - private String ollamaKey; + private static final Logger log = org.slf4j.LoggerFactory.getLogger(InternalOllamaLLMProvider.class); + private String ollamaKey; - private final WebClient.Builder webClientBuilder; - private String ollamaURL; + private final WebClient.Builder webClientBuilder; + private String ollamaURL; - public InternalOllamaLLMProvider(WebClient.Builder webClientBuilder, @Value("${app.ollama.internal.key}") String ollamakey, - @Value("${app.ollama.internal.url}") String ollamaUri) { - Objects.requireNonNull(ollamakey, "ollamaKey cannot be null"); - Objects.requireNonNull(ollamaUri, "ollamaUri cannot be null"); - this.ollamaKey = ollamakey; - this.ollamaURL = ollamaUri; + public InternalOllamaLLMProvider(WebClient.Builder webClientBuilder, + @Value("${app.ollama.internal.key}") String ollamakey, + @Value("${app.ollama.internal.url}") String ollamaUri) { + Objects.requireNonNull(ollamakey, "ollamaKey cannot be null"); + Objects.requireNonNull(ollamaUri, "ollamaUri cannot be null"); + this.ollamaKey = ollamakey; + this.ollamaURL = ollamaUri; - this.webClientBuilder = webClientBuilder; - } - - public String generate(String model, String prompt) { - Objects.requireNonNull(prompt, "prompt cannot be null"); - Objects.requireNonNull(model, "model cannot be null"); - ObjectMapper mapper = new ObjectMapper(); - Map bodyMap = Map.of( - "model", model, - "prompt", prompt, - "stream", false); - String requestBody = null; - try{ - requestBody = mapper.writeValueAsString(bodyMap); - }catch (Exception e) { - log.error("Error serializing request body: {}", e.getMessage(), e); - throw new RuntimeException("Error serializing request body", e); + this.webClientBuilder = webClientBuilder; } - log.debug("ollama body request: {} ", requestBody); - // Implement the logic to call the Ollama API and return the response - WebClient webClient = webClientBuilder.baseUrl(this.ollamaURL).build(); + @Override + public String getName() { + return "InternalOllamaLLMProvider"; + } - Mono result = webClient.post() - .uri(uriBuilder -> uriBuilder.pathSegment("generate") - .build()) - .header("Authorization", "Bearer " + ollamaKey) - .contentType(MediaType.APPLICATION_JSON) - .bodyValue(requestBody) - .retrieve() - .onStatus( - status -> status.is5xxServerError(), - clientResponse -> clientResponse.bodyToMono(String.class) - .defaultIfEmpty("Error: server without body") - .flatMap(body -> Mono.error(new RuntimeException("Error 5xx: " + body)))) - .bodyToMono(GenerateResponse.class) // deserialize JSON in oggetto Java - .timeout(Duration.ofMinutes(2)); + public String generate(String model, String prompt) { + Objects.requireNonNull(prompt, "prompt cannot be null"); + Objects.requireNonNull(model, "model cannot be null"); + ObjectMapper mapper = new ObjectMapper(); + Map bodyMap = Map.of( + "model", model, + "prompt", prompt, + "stream", false); + String requestBody = null; + try { + requestBody = mapper.writeValueAsString(bodyMap); + } catch (Exception e) { + log.error("Error serializing request body: {}", e.getMessage(), e); + throw new RuntimeException("Error serializing request body", e); + } + log.debug("ollama body request: {} ", requestBody); - return result.block().getResponse(); // Placeholder for actual implementation - } + // Implement the logic to call the Ollama API and return the response + WebClient webClient = webClientBuilder.baseUrl(this.ollamaURL).build(); - public List getRegisteredModels() { - WebClient webClient = webClientBuilder.baseUrl(this.ollamaURL).build(); + Mono result = webClient.post() + .uri(uriBuilder -> uriBuilder.pathSegment("generate") + .build()) + .header("Authorization", "Bearer " + ollamaKey) + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(requestBody) + .retrieve() + .onStatus( + status -> status.is5xxServerError(), + clientResponse -> clientResponse.bodyToMono(String.class) + .defaultIfEmpty("Error: server without body") + .flatMap(body -> Mono.error(new RuntimeException( + "Error 5xx: " + body)))) + .bodyToMono(GenerateResponse.class) // deserialize JSON in oggetto Java + .timeout(Duration.ofMinutes(2)); - Mono> result = webClient.get() - .uri(uriBuilder -> uriBuilder.pathSegment("tags") - .build()) - .header("Authorization", "Bearer " + ollamaKey) - .retrieve() - .onStatus( - status -> status.is5xxServerError(), - clientResponse -> clientResponse.bodyToMono(String.class) - .defaultIfEmpty("Error: server without body") - .flatMap(body -> Mono.error(new RuntimeException("Error 5xx: " + body)))) - .bodyToMono(ModelResponse.class) // deserialize JSON in oggetto Java - .timeout(Duration.ofMinutes(1)) - .map(modelResponse -> modelResponse.getModels().stream() - .map(ModelInfo::getName) - .toList()); + return result.block().getResponse(); // Placeholder for actual implementation + } - return result.block(); - } + public List getRegisteredModels() { + WebClient webClient = webClientBuilder.baseUrl(this.ollamaURL).build(); + Mono> result = webClient.get() + .uri(uriBuilder -> uriBuilder.pathSegment("tags") + .build()) + .header("Authorization", "Bearer " + ollamaKey) + .retrieve() + .onStatus( + status -> status.is5xxServerError(), + clientResponse -> clientResponse.bodyToMono(String.class) + .defaultIfEmpty("Error: server without body") + .flatMap(body -> Mono.error(new RuntimeException( + "Error 5xx: " + body)))) + .bodyToMono(ModelResponse.class) // deserialize JSON in oggetto Java + .timeout(Duration.ofMinutes(1)) + .map(modelResponse -> modelResponse.getModels().stream() + .map(ModelInfo::getName) + .toList()); + + return result.block(); + } } - diff --git a/src/test/java/it/cnr/isti/workflow/manager/MyTestConfiguration.java b/src/test/java/it/cnr/isti/workflow/manager/MyTestConfiguration.java index 3958c47..adbc236 100644 --- a/src/test/java/it/cnr/isti/workflow/manager/MyTestConfiguration.java +++ b/src/test/java/it/cnr/isti/workflow/manager/MyTestConfiguration.java @@ -8,9 +8,10 @@ import org.springframework.context.annotation.Profile; import it.cnr.isti.workflow.manager.bricks.Brick; import it.cnr.isti.workflow.manager.bricks.BrickManager; +import it.cnr.isti.workflow.manager.bricks.LLMBrick; +import it.cnr.isti.workflow.manager.llms.providers.LLMProvider; @TestConfiguration -@Profile("test") public class MyTestConfiguration { @Bean(name = "testProvider") @@ -27,4 +28,35 @@ public class MyTestConfiguration { }; } + @Bean(name = "llmBrickTestManager") + BrickManager llmBrickManagerProvider() { + return new BrickManager() { + @Override + public List getBricks() { + + LLMProvider provider = new LLMProvider() { + @Override + public String getName() { + return "llmTestProvider"; + } + + @Override + public List getRegisteredModels() { + return List.of("testModel"); + } + + @Override + public String generate(String model, String prompt) { + return "Generated response on " + model + " for prompt: " + prompt; + } + }; + + LLMBrick b = new LLMBrick(provider, "testModel", "testBrickManager"); + + return List.of(b); + } + + }; + } + } diff --git a/src/test/java/it/cnr/isti/workflow/manager/blocks/BlockTest.java b/src/test/java/it/cnr/isti/workflow/manager/blocks/BlockTest.java index c1931e7..7d0217d 100644 --- a/src/test/java/it/cnr/isti/workflow/manager/blocks/BlockTest.java +++ b/src/test/java/it/cnr/isti/workflow/manager/blocks/BlockTest.java @@ -7,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; import it.cnr.isti.workflow.manager.MyTestConfiguration; import it.cnr.isti.workflow.manager.blocks.configurations.LLMBlockConfiguration; @@ -23,7 +24,7 @@ public class BlockTest { @Autowired LLMBlockFactory llmBlockFactory; - @Qualifier("brickTestManager") + @Qualifier("llmBrickTestManager") @Autowired BrickManager llmBrickManager; @@ -34,7 +35,7 @@ public class BlockTest { @Test void createLLMBlock() { BlockFactory factory = llmBlockFactory; - LLMBrick brick = llmBrickManager.getBricks().getFirst(); + LLMBrick brick = (LLMBrick) llmBrickManager.getBricks().getFirst(); assertNotNull(factory); assertNotNull(brick); // Assuming LLMBlockConfiguration is a valid configuration class for @@ -44,7 +45,7 @@ public class BlockTest { .brick(brick) .name("master") .build(); - Block block = factory.create(config); + Block block = factory.create(config); System.out.println("Created block: " + block); } diff --git a/src/test/java/it/cnr/isti/workflow/manager/controllers/BlocksControllerTest.java b/src/test/java/it/cnr/isti/workflow/manager/controllers/BlocksControllerTest.java index 11a604a..44dd6c8 100644 --- a/src/test/java/it/cnr/isti/workflow/manager/controllers/BlocksControllerTest.java +++ b/src/test/java/it/cnr/isti/workflow/manager/controllers/BlocksControllerTest.java @@ -11,7 +11,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; -import org.springframework.test.context.ActiveProfiles; import it.cnr.isti.workflow.manager.MyTestConfiguration; import it.cnr.isti.workflow.manager.blocks.Block; @@ -23,11 +22,10 @@ import it.cnr.isti.workflow.manager.bricks.LLMBrick; @SpringBootTest @Import(MyTestConfiguration.class) -@ActiveProfiles("test") public class BlocksControllerTest { - @Qualifier("brickTestManager") + @Qualifier("llmBrickTestManager") @Autowired BrickManager llmBrickManager; @@ -51,7 +49,7 @@ public class BlocksControllerTest { .name("master") .brick(llmBrickManager.getBricks().get(0)) .build(); - Block block = blocksController.create(config); + Block block = blocksController.create(config); assertNotNull(block); assertTrue(block.getInputs().size() == 1); assertTrue(block.getInputs().getFirst().equals("name")); diff --git a/src/test/java/it/cnr/isti/workflow/manager/controllers/BricksControllerTest.java b/src/test/java/it/cnr/isti/workflow/manager/controllers/BricksControllerTest.java index 4ee0b79..e0ea1a0 100644 --- a/src/test/java/it/cnr/isti/workflow/manager/controllers/BricksControllerTest.java +++ b/src/test/java/it/cnr/isti/workflow/manager/controllers/BricksControllerTest.java @@ -17,7 +17,6 @@ import java.util.List; @SpringBootTest() @Import(MyTestConfiguration.class) -@ActiveProfiles("test") public class BricksControllerTest { @Autowired