From 7045ef78d3de9f22d01f6078864e4c55d4ce2725 Mon Sep 17 00:00:00 2001 From: fjtirado Date: Fri, 1 Aug 2025 10:55:26 +0200 Subject: [PATCH] Agentic core Signed-off-by: fjtirado --- experimental/agentic/pom.xml | 46 ++++++++++ .../agentic/AgenticExpressionFactory.java | 33 +++++++ .../expressions/agentic/AgenticModel.java | 52 +++++++++++ .../agentic/AgenticModelCollection.java | 53 +++++++++++ .../agentic/AgenticModelFactory.java | 90 +++++++++++++++++++ ...orkflow.impl.expressions.ExpressionFactory | 1 + .../func/JavaForExecutorBuilder.java | 5 +- .../impl/executors/func/JavaFuncUtils.java | 25 ++++++ .../func/JavaFunctionCallExecutor.java | 16 +++- .../impl/expressions/func/JavaModel.java | 10 +-- .../expressions/func/JavaModelCollection.java | 14 +-- .../expressions/func/JavaModelFactory.java | 7 +- experimental/pom.xml | 5 +- .../api/types/func/CallJava.java | 18 +++- .../api/types/func/ForTaskFunction.java | 42 ++++++++- fluent/agentic/pom.xml | 5 +- .../agentic/AgentTaskItemListBuilder.java | 8 +- .../fluent/agentic/Agents.java | 10 ++- .../fluent/agentic/WorkflowTests.java | 38 +++++--- .../fluent/func/FuncCallTaskBuilder.java | 6 +- .../fluent/func/FuncForkTaskBuilder.java | 9 +- .../impl/ServicePriority.java | 30 +++++++ .../impl/WorkflowModelFactory.java | 8 +- .../impl/executors/TaskExecutorFactory.java | 3 +- .../impl/expressions/ExpressionFactory.java | 3 +- .../impl/schema/SchemaValidatorFactory.java | 3 +- pom.xml | 7 +- 27 files changed, 490 insertions(+), 57 deletions(-) create mode 100644 experimental/agentic/pom.xml create mode 100644 experimental/agentic/src/main/java/io/serverlessworkflow/impl/expressions/agentic/AgenticExpressionFactory.java create mode 100644 experimental/agentic/src/main/java/io/serverlessworkflow/impl/expressions/agentic/AgenticModel.java create mode 100644 experimental/agentic/src/main/java/io/serverlessworkflow/impl/expressions/agentic/AgenticModelCollection.java create mode 100644 experimental/agentic/src/main/java/io/serverlessworkflow/impl/expressions/agentic/AgenticModelFactory.java create mode 100644 experimental/agentic/src/main/resources/META-INF/services/io.serverlessworkflow.impl.expressions.ExpressionFactory create mode 100644 impl/core/src/main/java/io/serverlessworkflow/impl/ServicePriority.java diff --git a/experimental/agentic/pom.xml b/experimental/agentic/pom.xml new file mode 100644 index 00000000..dec8a5e5 --- /dev/null +++ b/experimental/agentic/pom.xml @@ -0,0 +1,46 @@ + + 4.0.0 + + io.serverlessworkflow + serverlessworkflow-experimental + 8.0.0-SNAPSHOT + + serverlessworkflow-experimental-agentic + ServelessWorkflow:: Experimental:: Agentic + + + io.serverlessworkflow + serverlessworkflow-experimental-lambda + + + dev.langchain4j + langchain4j-agentic + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.assertj + assertj-core + test + + + ch.qos.logback + logback-classic + test + + + \ No newline at end of file diff --git a/experimental/agentic/src/main/java/io/serverlessworkflow/impl/expressions/agentic/AgenticExpressionFactory.java b/experimental/agentic/src/main/java/io/serverlessworkflow/impl/expressions/agentic/AgenticExpressionFactory.java new file mode 100644 index 00000000..a4b79a18 --- /dev/null +++ b/experimental/agentic/src/main/java/io/serverlessworkflow/impl/expressions/agentic/AgenticExpressionFactory.java @@ -0,0 +1,33 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.impl.expressions.agentic; + +import io.serverlessworkflow.impl.WorkflowModelFactory; +import io.serverlessworkflow.impl.expressions.func.JavaExpressionFactory; + +public class AgenticExpressionFactory extends JavaExpressionFactory { + + private final WorkflowModelFactory modelFactory = new AgenticModelFactory(); + + @Override + public WorkflowModelFactory modelFactory() { + return modelFactory; + } + + public int priority() { + return DEFAULT_PRIORITY - 1; + } +} diff --git a/experimental/agentic/src/main/java/io/serverlessworkflow/impl/expressions/agentic/AgenticModel.java b/experimental/agentic/src/main/java/io/serverlessworkflow/impl/expressions/agentic/AgenticModel.java new file mode 100644 index 00000000..9ab57838 --- /dev/null +++ b/experimental/agentic/src/main/java/io/serverlessworkflow/impl/expressions/agentic/AgenticModel.java @@ -0,0 +1,52 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.impl.expressions.agentic; + +import dev.langchain4j.agentic.cognisphere.Cognisphere; +import dev.langchain4j.agentic.cognisphere.ResultWithCognisphere; +import io.serverlessworkflow.impl.WorkflowModel; +import io.serverlessworkflow.impl.expressions.func.JavaModel; +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; + +class AgenticModel extends JavaModel { + + private final Cognisphere cognisphere; + + AgenticModel(Object object, Cognisphere cognisphere) { + super(object); + this.cognisphere = cognisphere; + } + + @Override + public Collection asCollection() { + return object instanceof Collection value + ? new AgenticModelCollection(value, cognisphere) + : Collections.emptyList(); + } + + @Override + public Optional as(Class clazz) { + if (Cognisphere.class.isAssignableFrom(clazz)) { + return Optional.of(clazz.cast(cognisphere)); + } else if (ResultWithCognisphere.class.isAssignableFrom(clazz)) { + return Optional.of(clazz.cast(new ResultWithCognisphere<>(cognisphere, object))); + } else { + return super.as(clazz); + } + } +} diff --git a/experimental/agentic/src/main/java/io/serverlessworkflow/impl/expressions/agentic/AgenticModelCollection.java b/experimental/agentic/src/main/java/io/serverlessworkflow/impl/expressions/agentic/AgenticModelCollection.java new file mode 100644 index 00000000..e9440fb5 --- /dev/null +++ b/experimental/agentic/src/main/java/io/serverlessworkflow/impl/expressions/agentic/AgenticModelCollection.java @@ -0,0 +1,53 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.impl.expressions.agentic; + +import dev.langchain4j.agentic.cognisphere.Cognisphere; +import dev.langchain4j.agentic.cognisphere.ResultWithCognisphere; +import io.serverlessworkflow.impl.WorkflowModel; +import io.serverlessworkflow.impl.expressions.func.JavaModelCollection; +import java.util.Collection; +import java.util.Optional; + +class AgenticModelCollection extends JavaModelCollection { + + private final Cognisphere cognisphere; + + AgenticModelCollection(Collection object, Cognisphere cognisphere) { + super(object); + this.cognisphere = cognisphere; + } + + AgenticModelCollection(Cognisphere cognisphere) { + this.cognisphere = cognisphere; + } + + @Override + protected WorkflowModel nextItem(Object obj) { + return new AgenticModel(obj, cognisphere); + } + + @Override + public Optional as(Class clazz) { + if (Cognisphere.class.isAssignableFrom(clazz)) { + return Optional.of(clazz.cast(cognisphere)); + } else if (ResultWithCognisphere.class.isAssignableFrom(clazz)) { + return Optional.of(clazz.cast(new ResultWithCognisphere<>(cognisphere, object))); + } else { + return super.as(clazz); + } + } +} diff --git a/experimental/agentic/src/main/java/io/serverlessworkflow/impl/expressions/agentic/AgenticModelFactory.java b/experimental/agentic/src/main/java/io/serverlessworkflow/impl/expressions/agentic/AgenticModelFactory.java new file mode 100644 index 00000000..dc7c53ac --- /dev/null +++ b/experimental/agentic/src/main/java/io/serverlessworkflow/impl/expressions/agentic/AgenticModelFactory.java @@ -0,0 +1,90 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.impl.expressions.agentic; + +import dev.langchain4j.agentic.cognisphere.Cognisphere; +import dev.langchain4j.agentic.cognisphere.CognisphereRegistry; +import io.cloudevents.CloudEvent; +import io.cloudevents.CloudEventData; +import io.serverlessworkflow.impl.WorkflowModel; +import io.serverlessworkflow.impl.WorkflowModelCollection; +import io.serverlessworkflow.impl.WorkflowModelFactory; +import java.time.OffsetDateTime; +import java.util.Map; + +class AgenticModelFactory implements WorkflowModelFactory { + private final Cognisphere cognisphere = CognisphereRegistry.createEphemeralCognisphere(); + + private final AgenticModel TrueModel = new AgenticModel(Boolean.TRUE, cognisphere); + private final AgenticModel FalseModel = new AgenticModel(Boolean.FALSE, cognisphere); + private final AgenticModel NullModel = new AgenticModel(null, cognisphere); + + @Override + public WorkflowModel combine(Map workflowVariables) { + return new AgenticModel(workflowVariables, cognisphere); + } + + @Override + public WorkflowModelCollection createCollection() { + return new AgenticModelCollection(cognisphere); + } + + @Override + public WorkflowModel from(boolean value) { + return value ? TrueModel : FalseModel; + } + + @Override + public WorkflowModel from(Number value) { + return new AgenticModel(value, cognisphere); + } + + @Override + public WorkflowModel from(String value) { + return new AgenticModel(value, cognisphere); + } + + @Override + public WorkflowModel from(CloudEvent ce) { + return new AgenticModel(ce, cognisphere); + } + + @Override + public WorkflowModel from(CloudEventData ce) { + return new AgenticModel(ce, cognisphere); + } + + @Override + public WorkflowModel from(OffsetDateTime value) { + return new AgenticModel(value, cognisphere); + } + + @Override + public WorkflowModel from(Map map) { + cognisphere.writeStates(map); + return new AgenticModel(map, cognisphere); + } + + @Override + public WorkflowModel fromNull() { + return NullModel; + } + + @Override + public WorkflowModel fromOther(Object value) { + return new AgenticModel(value, cognisphere); + } +} diff --git a/experimental/agentic/src/main/resources/META-INF/services/io.serverlessworkflow.impl.expressions.ExpressionFactory b/experimental/agentic/src/main/resources/META-INF/services/io.serverlessworkflow.impl.expressions.ExpressionFactory new file mode 100644 index 00000000..e0038ed9 --- /dev/null +++ b/experimental/agentic/src/main/resources/META-INF/services/io.serverlessworkflow.impl.expressions.ExpressionFactory @@ -0,0 +1 @@ +io.serverlessworkflow.impl.expressions.agentic.AgenticExpressionFactory \ No newline at end of file diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaForExecutorBuilder.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaForExecutorBuilder.java index f58ce510..9723fba7 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaForExecutorBuilder.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaForExecutorBuilder.java @@ -43,7 +43,8 @@ protected JavaForExecutorBuilder( protected Optional buildWhileFilter() { if (task instanceof ForTaskFunction taskFunctions) { - LoopPredicateIndex whilePred = taskFunctions.getWhilePredicate(); + final LoopPredicateIndex whilePred = taskFunctions.getWhilePredicate(); + Optional> modelClass = taskFunctions.getModelClass(); String varName = task.getFor().getEach(); String indexName = task.getFor().getAt(); if (whilePred != null) { @@ -54,7 +55,7 @@ protected Optional buildWhileFilter() { .modelFactory() .from( whilePred.test( - n.asJavaObject(), + JavaFuncUtils.convert(n, modelClass), item, (Integer) safeObject(t.variables().get(indexName)))); }); diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaFuncUtils.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaFuncUtils.java index 33b90fb9..ed42bf50 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaFuncUtils.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaFuncUtils.java @@ -16,6 +16,7 @@ package io.serverlessworkflow.impl.executors.func; import io.serverlessworkflow.impl.WorkflowModel; +import java.util.Optional; public class JavaFuncUtils { @@ -23,5 +24,29 @@ static Object safeObject(Object obj) { return obj instanceof WorkflowModel model ? model.asJavaObject() : obj; } + static T convertT(WorkflowModel model, Optional> inputClass) { + return inputClass + .map( + c -> + model + .as(c) + .orElseThrow( + () -> + new IllegalArgumentException( + "Model " + model + " cannot be converted to type " + c))) + .orElseGet(() -> (T) model.asJavaObject()); + } + + static Object convert(WorkflowModel model, Optional> inputClass) { + return inputClass.isPresent() + ? model + .as(inputClass.orElseThrow()) + .orElseThrow( + () -> + new IllegalArgumentException( + "Model " + model + " cannot be converted to type " + inputClass)) + : model.asJavaObject(); + } + private JavaFuncUtils() {} } diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaFunctionCallExecutor.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaFunctionCallExecutor.java index 8a9f219a..9b040772 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaFunctionCallExecutor.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaFunctionCallExecutor.java @@ -25,19 +25,27 @@ import io.serverlessworkflow.impl.WorkflowModelFactory; import io.serverlessworkflow.impl.executors.CallableTask; import io.serverlessworkflow.impl.resources.ResourceLoader; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Function; -public class JavaFunctionCallExecutor implements CallableTask { +public class JavaFunctionCallExecutor + implements CallableTask> { - private Function function; + private Function function; + private Optional> inputClass; + + static String fromInt(Integer integer) { + return Integer.toString(integer); + } public void init( - CallJava.CallJavaFunction task, + CallJava.CallJavaFunction task, Workflow workflow, WorkflowApplication application, ResourceLoader loader) { function = task.function(); + inputClass = task.inputClass(); } @Override @@ -45,7 +53,7 @@ public CompletableFuture apply( WorkflowContext workflowContext, TaskContext taskContext, WorkflowModel input) { WorkflowModelFactory modelFactory = workflowContext.definition().application().modelFactory(); return CompletableFuture.completedFuture( - modelFactory.fromAny(function.apply(input.asJavaObject()))); + modelFactory.fromAny(function.apply(JavaFuncUtils.convertT(input, inputClass)))); } @Override diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaModel.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaModel.java index bb236ed7..8c2992f2 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaModel.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaModel.java @@ -26,15 +26,11 @@ import java.util.function.BiConsumer; import java.util.stream.Collectors; -class JavaModel implements WorkflowModel { +public class JavaModel implements WorkflowModel { - private Object object; + protected final Object object; - static final JavaModel TrueModel = new JavaModel(Boolean.TRUE); - static final JavaModel FalseModel = new JavaModel(Boolean.FALSE); - static final JavaModel NullModel = new JavaModel(null); - - JavaModel(Object object) { + protected JavaModel(Object object) { this.object = asJavaObject(object); } diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaModelCollection.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaModelCollection.java index 0b9c914f..12e0ab66 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaModelCollection.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaModelCollection.java @@ -22,15 +22,15 @@ import java.util.Iterator; import java.util.Optional; -class JavaModelCollection implements Collection, WorkflowModelCollection { +public class JavaModelCollection implements Collection, WorkflowModelCollection { - private final Collection object; + protected final Collection object; - JavaModelCollection() { + protected JavaModelCollection() { this.object = new ArrayList<>(); } - JavaModelCollection(Collection object) { + protected JavaModelCollection(Collection object) { this.object = (Collection) JavaModel.asJavaObject(object); } @@ -65,10 +65,14 @@ public boolean hasNext() { @Override public WorkflowModel next() { Object obj = wrapped.next(); - return obj instanceof WorkflowModel value ? value : new JavaModel(obj); + return obj instanceof WorkflowModel value ? value : nextItem(obj); } } + protected WorkflowModel nextItem(Object obj) { + return new JavaModel(obj); + } + @Override public Iterator iterator() { return new ModelIterator(object.iterator()); diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaModelFactory.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaModelFactory.java index 59975e50..2ccc0dcd 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaModelFactory.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaModelFactory.java @@ -24,6 +24,9 @@ import java.util.Map; class JavaModelFactory implements WorkflowModelFactory { + private final JavaModel TrueModel = new JavaModel(Boolean.TRUE); + private final JavaModel FalseModel = new JavaModel(Boolean.FALSE); + private final JavaModel NullModel = new JavaModel(null); @Override public WorkflowModel combine(Map workflowVariables) { @@ -37,7 +40,7 @@ public WorkflowModelCollection createCollection() { @Override public WorkflowModel from(boolean value) { - return value ? JavaModel.TrueModel : JavaModel.FalseModel; + return value ? TrueModel : FalseModel; } @Override @@ -72,7 +75,7 @@ public WorkflowModel from(Map map) { @Override public WorkflowModel fromNull() { - return JavaModel.NullModel; + return NullModel; } @Override diff --git a/experimental/pom.xml b/experimental/pom.xml index c4b61781..a8dbc874 100644 --- a/experimental/pom.xml +++ b/experimental/pom.xml @@ -38,8 +38,9 @@ - types - lambda + types + lambda + agentic lambda-fluent \ No newline at end of file diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/CallJava.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/CallJava.java index 4158ee57..6423fdae 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/CallJava.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/CallJava.java @@ -18,6 +18,7 @@ import io.serverlessworkflow.api.types.TaskBase; import io.serverlessworkflow.impl.expressions.LoopFunction; import io.serverlessworkflow.impl.expressions.LoopFunctionIndex; +import java.util.Optional; import java.util.function.Consumer; import java.util.function.Function; @@ -29,8 +30,13 @@ public static CallJava consumer(Consumer consumer) { return new CallJavaConsumer<>(consumer); } - public static CallJava function(Function function) { - return new CallJavaFunction<>(function); + public static CallJavaFunction function(Function function) { + return new CallJavaFunction<>(function, Optional.empty()); + } + + public static CallJavaFunction function( + Function function, Class inputClass) { + return new CallJavaFunction<>(function, Optional.ofNullable(inputClass)); } public static CallJava loopFunction( @@ -60,14 +66,20 @@ public static class CallJavaFunction extends CallJava { private static final long serialVersionUID = 1L; private Function function; + private Optional> inputClass; - public CallJavaFunction(Function function) { + public CallJavaFunction(Function function, Optional> inputClass) { this.function = function; + this.inputClass = inputClass; } public Function function() { return function; } + + public Optional> inputClass() { + return inputClass; + } } public static class CallJavaLoopFunction extends CallJava { diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ForTaskFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ForTaskFunction.java index 779dccd4..8c9ad956 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ForTaskFunction.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ForTaskFunction.java @@ -19,17 +19,28 @@ import io.serverlessworkflow.impl.expressions.LoopPredicate; import io.serverlessworkflow.impl.expressions.LoopPredicateIndex; import java.util.Collection; +import java.util.Optional; import java.util.function.Function; public class ForTaskFunction extends ForTask { private static final long serialVersionUID = 1L; private LoopPredicateIndex whilePredicate; + private Optional> modelClass; + private Optional> itemClass; private Function> collection; public ForTaskFunction withWhile(LoopPredicate whilePredicate) { - this.whilePredicate = toPredicateIndex(whilePredicate); - return this; + return withWhile(toPredicateIndex(whilePredicate)); + } + + public ForTaskFunction withWhile(LoopPredicate whilePredicate, Class modelClass) { + return withWhile(toPredicateIndex(whilePredicate), modelClass); + } + + public ForTaskFunction withWhile( + LoopPredicate whilePredicate, Class modelClass, Class itemClass) { + return withWhile(toPredicateIndex(whilePredicate), modelClass, itemClass); } private LoopPredicateIndex toPredicateIndex(LoopPredicate whilePredicate) { @@ -37,7 +48,26 @@ private LoopPredicateIndex toPredicateIndex(LoopPredicate whi } public ForTaskFunction withWhile(LoopPredicateIndex whilePredicate) { + return withWhile(whilePredicate, Optional.empty(), Optional.empty()); + } + + public ForTaskFunction withWhile( + LoopPredicateIndex whilePredicate, Class modelClass) { + return withWhile(whilePredicate, Optional.of(modelClass), Optional.empty()); + } + + public ForTaskFunction withWhile( + LoopPredicateIndex whilePredicate, Class modelClass, Class itemClass) { + return withWhile(whilePredicate, Optional.of(modelClass), Optional.of(itemClass)); + } + + private ForTaskFunction withWhile( + LoopPredicateIndex whilePredicate, + Optional> modelClass, + Optional> itemClass) { this.whilePredicate = whilePredicate; + this.modelClass = modelClass; + this.itemClass = itemClass; return this; } @@ -50,6 +80,14 @@ public ForTaskFunction withCollection(Function> collection) return whilePredicate; } + public Optional> getModelClass() { + return modelClass; + } + + public Optional> getItemClass() { + return itemClass; + } + public Function> getCollection() { return collection; } diff --git a/fluent/agentic/pom.xml b/fluent/agentic/pom.xml index 02d1847d..849bf747 100644 --- a/fluent/agentic/pom.xml +++ b/fluent/agentic/pom.xml @@ -16,8 +16,6 @@ 17 17 UTF-8 - - 1.2.0-beta8-SNAPSHOT @@ -32,7 +30,6 @@ dev.langchain4j langchain4j-agentic - ${version.dev.langchain4j} org.slf4j @@ -62,7 +59,7 @@ io.serverlessworkflow - serverlessworkflow-experimental-lambda + serverlessworkflow-experimental-agentic ${project.version} test diff --git a/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/AgentTaskItemListBuilder.java b/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/AgentTaskItemListBuilder.java index 5aed3127..60329e43 100644 --- a/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/AgentTaskItemListBuilder.java +++ b/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/AgentTaskItemListBuilder.java @@ -15,6 +15,7 @@ */ package io.serverlessworkflow.fluent.agentic; +import dev.langchain4j.agentic.cognisphere.Cognisphere; import dev.langchain4j.agentic.internal.AgentExecutor; import io.serverlessworkflow.api.types.Task; import io.serverlessworkflow.api.types.TaskItem; @@ -54,7 +55,9 @@ protected AgentTaskItemListBuilder newItemListBuilder() { public AgentTaskItemListBuilder agent(String name, Object agent) { AgentAdapters.toExecutors(agent) .forEach( - exec -> this.delegate.callFn(name, fn -> fn.function(AgentAdapters.toFunction(exec)))); + exec -> + this.delegate.callFn( + name, fn -> fn.function(AgentAdapters.toFunction(exec), Cognisphere.class))); return self(); } @@ -82,7 +85,8 @@ public AgentTaskItemListBuilder parallel(String name, Object... agents) { List execs = AgentAdapters.toExecutors(agents); for (int i = 0; i < execs.size(); i++) { AgentExecutor ex = execs.get(i); - fork.branch("branch-" + i + "-" + name, AgentAdapters.toFunction(ex)); + fork.branch( + "branch-" + i + "-" + name, AgentAdapters.toFunction(ex), Cognisphere.class); } }); return self(); diff --git a/fluent/agentic/src/test/java/io/serverlessworkflow/fluent/agentic/Agents.java b/fluent/agentic/src/test/java/io/serverlessworkflow/fluent/agentic/Agents.java index 8ba9d8c7..7215ddd6 100644 --- a/fluent/agentic/src/test/java/io/serverlessworkflow/fluent/agentic/Agents.java +++ b/fluent/agentic/src/test/java/io/serverlessworkflow/fluent/agentic/Agents.java @@ -153,7 +153,12 @@ interface MeetingInvitationDraft extends AgentInstance { Agenda: {{agenda}} """) @Agent("Drafts a professional meeting invitation email.") - String invoke(@V("subject") String subject, @V("date") String date, @V("time") String time, @V("location") String location, @V("agenda") String agenda); + String invoke( + @V("subject") String subject, + @V("date") String date, + @V("time") String time, + @V("location") String location, + @V("agenda") String agenda); } interface MeetingInvitationStyle extends AgentInstance { @@ -166,5 +171,4 @@ interface MeetingInvitationStyle extends AgentInstance { @Agent("Edits a meeting invitation email to better fit a given style.") String invoke(@V("invitation") String invitation, @V("style") String style); } - - } +} diff --git a/fluent/agentic/src/test/java/io/serverlessworkflow/fluent/agentic/WorkflowTests.java b/fluent/agentic/src/test/java/io/serverlessworkflow/fluent/agentic/WorkflowTests.java index 0071d0bd..8bbbe62d 100644 --- a/fluent/agentic/src/test/java/io/serverlessworkflow/fluent/agentic/WorkflowTests.java +++ b/fluent/agentic/src/test/java/io/serverlessworkflow/fluent/agentic/WorkflowTests.java @@ -35,6 +35,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.stream.Collectors; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; class WorkflowTests { @@ -225,16 +226,18 @@ public void testSeqAndThenParallel() throws ExecutionException, InterruptedExcep } @Test + @Disabled("HumanLoop not implemented yet") public void humanInTheLoop() throws ExecutionException, InterruptedException { final MeetingInvitationDraft meetingInvitationDraft = mock(MeetingInvitationDraft.class); - when(meetingInvitationDraft.invoke(eq("Meeting with John Doe"), - eq("2023-10-01"), eq("08:00AM"), + when(meetingInvitationDraft.invoke( + eq("Meeting with John Doe"), + eq("2023-10-01"), + eq("08:00AM"), eq("London"), eq("Discuss project updates"))) .thenReturn("Drafted meeting invitation for John Doe"); when(meetingInvitationDraft.outputName()).thenReturn("draft"); - final MeetingInvitationStyle meetingInvitationStyle = mock(MeetingInvitationStyle.class); when(meetingInvitationStyle.invoke(eq("Drafted meeting invitation for John Doe"), eq("formal"))) .thenReturn("Styled meeting invitation for John Doe"); @@ -242,11 +245,16 @@ public void humanInTheLoop() throws ExecutionException, InterruptedException { AtomicReference request = new AtomicReference<>(); - HumanInTheLoop humanInTheLoop = AgentServices.humanInTheLoopBuilder() - .description("What level of formality would you like? (please reply with “formal”, “casual”, or “friendly”)") + HumanInTheLoop humanInTheLoop = + AgentServices.humanInTheLoopBuilder() + .description( + "What level of formality would you like? (please reply with “formal”, “casual”, or “friendly”)") .inputName("style") .outputName("style") - .requestWriter(q -> request.set("What level of formality would you like? (please reply with “formal”, “casual”, or “friendly”)")) + .requestWriter( + q -> + request.set( + "What level of formality would you like? (please reply with “formal”, “casual”, or “friendly”)")) .responseReader(() -> "formal") .build(); @@ -254,8 +262,9 @@ public void humanInTheLoop() throws ExecutionException, InterruptedException { AgentWorkflowBuilder.workflow("meetingInvitationFlow") .tasks( d -> - d.sequence("draft", meetingInvitationDraft, humanInTheLoop, meetingInvitationStyle) - ).build(); + d.sequence( + "draft", meetingInvitationDraft, humanInTheLoop, meetingInvitationStyle)) + .build(); Map initialValues = new HashMap<>(); initialValues.put("title", "Meeting with John Doe"); initialValues.put("date", "2023-10-01"); @@ -265,11 +274,14 @@ public void humanInTheLoop() throws ExecutionException, InterruptedException { try (WorkflowApplication app = WorkflowApplication.builder().build()) { String result = - app.workflowDefinition(workflow).instance(initialValues).start().get().asText().orElseThrow(); - - assertEquals("Styled meeting invitation for John Doe", result); + app.workflowDefinition(workflow) + .instance(initialValues) + .start() + .get() + .asText() + .orElseThrow(); + + assertEquals("Styled meeting invitation for John Doe", result); } - - } } diff --git a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncCallTaskBuilder.java b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncCallTaskBuilder.java index 6e1da87f..30d874f0 100644 --- a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncCallTaskBuilder.java +++ b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncCallTaskBuilder.java @@ -39,7 +39,11 @@ protected FuncCallTaskBuilder self() { } public FuncCallTaskBuilder function(Function function) { - this.callTaskJava = new CallTaskJava(CallJava.function(function)); + return function(function, null); + } + + public FuncCallTaskBuilder function(Function function, Class argClass) { + this.callTaskJava = new CallTaskJava(CallJava.function(function, argClass)); super.setTask(this.callTaskJava.getCallJava()); return this; } diff --git a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncForkTaskBuilder.java b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncForkTaskBuilder.java index a4326276..372da744 100644 --- a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncForkTaskBuilder.java +++ b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncForkTaskBuilder.java @@ -51,8 +51,15 @@ protected FuncForkTaskBuilder self() { } public FuncForkTaskBuilder branch(String name, Function function) { + return branch(name, function, null); + } + + public FuncForkTaskBuilder branch( + String name, Function function, Class argParam) { this.items.add( - new TaskItem(name, new Task().withCallTask(new CallTaskJava(CallJava.function(function))))); + new TaskItem( + name, + new Task().withCallTask(new CallTaskJava(CallJava.function(function, argParam))))); return this; } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/ServicePriority.java b/impl/core/src/main/java/io/serverlessworkflow/impl/ServicePriority.java new file mode 100644 index 00000000..74b5bdf1 --- /dev/null +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/ServicePriority.java @@ -0,0 +1,30 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.impl; + +public interface ServicePriority extends Comparable { + + static final int DEFAULT_PRIORITY = 1000; + + default int priority() { + return DEFAULT_PRIORITY; + } + + @Override + default int compareTo(ServicePriority other) { + return this.priority() - other.priority(); + } +} diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowModelFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowModelFactory.java index 4c55723a..25564bc0 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowModelFactory.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowModelFactory.java @@ -42,6 +42,11 @@ public interface WorkflowModelFactory { WorkflowModel fromNull(); + default WorkflowModel fromOther(Object obj) { + throw new IllegalArgumentException( + "Unsupported conversion for object " + obj + " of type" + obj.getClass()); + } + default WorkflowModel fromAny(Object obj) { if (obj == null) { return fromNull(); @@ -62,8 +67,7 @@ default WorkflowModel fromAny(Object obj) { } else if (obj instanceof WorkflowModel model) { return model; } else { - throw new IllegalArgumentException( - "Unsopported conversion for object " + obj + " of type" + obj.getClass()); + return fromOther(obj); } } } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/TaskExecutorFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/TaskExecutorFactory.java index b1be3429..be1f1bee 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/TaskExecutorFactory.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/TaskExecutorFactory.java @@ -18,11 +18,12 @@ import io.serverlessworkflow.api.types.Task; import io.serverlessworkflow.api.types.TaskBase; import io.serverlessworkflow.api.types.Workflow; +import io.serverlessworkflow.impl.ServicePriority; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowPosition; import io.serverlessworkflow.impl.resources.ResourceLoader; -public interface TaskExecutorFactory { +public interface TaskExecutorFactory extends ServicePriority { TaskExecutorBuilder getTaskExecutor( WorkflowPosition position, Task task, diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ExpressionFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ExpressionFactory.java index cb14555e..28039cc7 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ExpressionFactory.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ExpressionFactory.java @@ -16,11 +16,12 @@ package io.serverlessworkflow.impl.expressions; import io.serverlessworkflow.api.types.TaskBase; +import io.serverlessworkflow.impl.ServicePriority; import io.serverlessworkflow.impl.WorkflowFilter; import io.serverlessworkflow.impl.WorkflowModelFactory; import java.util.Optional; -public interface ExpressionFactory { +public interface ExpressionFactory extends ServicePriority { /** * @throws ExpressionValidationException * @param expression diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/schema/SchemaValidatorFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/schema/SchemaValidatorFactory.java index 56b4b079..ff57ef5d 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/schema/SchemaValidatorFactory.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/schema/SchemaValidatorFactory.java @@ -16,9 +16,10 @@ package io.serverlessworkflow.impl.schema; import io.serverlessworkflow.api.types.SchemaInline; +import io.serverlessworkflow.impl.ServicePriority; import io.serverlessworkflow.impl.resources.StaticResource; -public interface SchemaValidatorFactory { +public interface SchemaValidatorFactory extends ServicePriority { SchemaValidator getValidator(SchemaInline inline); SchemaValidator getValidator(StaticResource resource); diff --git a/pom.xml b/pom.xml index 428e82bb..471a0028 100644 --- a/pom.xml +++ b/pom.xml @@ -86,7 +86,7 @@ 2.0.17 9.0.1.Final 6.0.0 - + 1.3.0-beta9-SNAPSHOT true ${version.org.assertj} test + + dev.langchain4j + langchain4j-agentic + ${version.dev.langchain4j} +