diff --git a/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/AgentWorkflowBuilder.java b/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/AgentWorkflowBuilder.java index e0eeb0c6..b17c35cc 100644 --- a/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/AgentWorkflowBuilder.java +++ b/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/AgentWorkflowBuilder.java @@ -15,9 +15,11 @@ */ package io.serverlessworkflow.fluent.agentic; +import dev.langchain4j.agentic.scope.AgenticScope; import io.serverlessworkflow.fluent.func.spi.FuncTransformations; import io.serverlessworkflow.fluent.spec.BaseWorkflowBuilder; import java.util.UUID; +import java.util.function.Predicate; public class AgentWorkflowBuilder extends BaseWorkflowBuilder @@ -40,6 +42,40 @@ public static AgentWorkflowBuilder workflow(String name, String ns) { return new AgentWorkflowBuilder(name, ns, DEFAULT_VERSION); } + public AgentWorkflowBuilder sequence(Object... agents) { + return sequence(UUID.randomUUID().toString(), agents); + } + + public AgentWorkflowBuilder sequence(String name, Object... agents) { + final AgentDoTaskBuilder doTaskBuilder = this.newDo(); + doTaskBuilder.sequence(name, agents); + this.workflow.setDo(doTaskBuilder.build().getDo()); + return this; + } + + public AgentWorkflowBuilder parallel(Object... agents) { + return this.parallel(UUID.randomUUID().toString(), agents); + } + + public AgentWorkflowBuilder parallel(String name, Object... agents) { + final AgentDoTaskBuilder doTaskBuilder = this.newDo(); + doTaskBuilder.parallel(name, agents); + this.workflow.setDo(doTaskBuilder.build().getDo()); + return this; + } + + public AgentWorkflowBuilder loop(Predicate exitCondition, Object... agents) { + return this.loop(UUID.randomUUID().toString(), exitCondition, agents); + } + + public AgentWorkflowBuilder loop( + String name, Predicate exitCondition, Object... agents) { + final AgentDoTaskBuilder doTaskBuilder = this.newDo(); + doTaskBuilder.loop(name, loop -> loop.subAgents(agents).exitCondition(exitCondition)); + this.workflow.setDo(doTaskBuilder.build().getDo()); + return this; + } + @Override protected AgentDoTaskBuilder newDo() { return new AgentDoTaskBuilder(); diff --git a/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/configurer/AgentTaskConfigurer.java b/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/configurer/AgentTaskConfigurer.java new file mode 100644 index 00000000..9473eb1a --- /dev/null +++ b/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/configurer/AgentTaskConfigurer.java @@ -0,0 +1,22 @@ +/* + * 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.fluent.agentic.configurer; + +import io.serverlessworkflow.fluent.agentic.AgentDoTaskBuilder; +import java.util.function.Consumer; + +@FunctionalInterface +public interface AgentTaskConfigurer extends Consumer {} diff --git a/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/configurer/FuncPredicateEventConfigurer.java b/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/configurer/FuncPredicateEventConfigurer.java new file mode 100644 index 00000000..fce8875d --- /dev/null +++ b/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/configurer/FuncPredicateEventConfigurer.java @@ -0,0 +1,23 @@ +/* + * 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.fluent.agentic.configurer; + +import io.serverlessworkflow.fluent.func.FuncPredicateEventPropertiesBuilder; +import java.util.function.Consumer; + +@FunctionalInterface +public interface FuncPredicateEventConfigurer + extends Consumer {} diff --git a/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/configurer/ListenConfigurer.java b/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/configurer/ListenConfigurer.java new file mode 100644 index 00000000..b8db664c --- /dev/null +++ b/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/configurer/ListenConfigurer.java @@ -0,0 +1,22 @@ +/* + * 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.fluent.agentic.configurer; + +import io.serverlessworkflow.fluent.agentic.AgentListenTaskBuilder; +import java.util.function.Consumer; + +@FunctionalInterface +public interface ListenConfigurer extends Consumer {} diff --git a/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/configurer/SwitchCaseConfigurer.java b/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/configurer/SwitchCaseConfigurer.java new file mode 100644 index 00000000..3e302ff2 --- /dev/null +++ b/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/configurer/SwitchCaseConfigurer.java @@ -0,0 +1,23 @@ +/* + * 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.fluent.agentic.configurer; + +import io.serverlessworkflow.fluent.func.FuncSwitchTaskBuilder; +import java.util.function.Consumer; + +@FunctionalInterface +public interface SwitchCaseConfigurer + extends Consumer {} diff --git a/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/dsl/AgenticDSL.java b/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/dsl/AgenticDSL.java new file mode 100644 index 00000000..052dd807 --- /dev/null +++ b/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/dsl/AgenticDSL.java @@ -0,0 +1,150 @@ +/* + * 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.fluent.agentic.dsl; + +import dev.langchain4j.agentic.scope.AgenticScope; +import io.cloudevents.CloudEventData; +import io.serverlessworkflow.api.types.FlowDirectiveEnum; +import io.serverlessworkflow.fluent.agentic.AgentDoTaskBuilder; +import io.serverlessworkflow.fluent.agentic.configurer.AgentTaskConfigurer; +import io.serverlessworkflow.fluent.agentic.configurer.FuncPredicateEventConfigurer; +import io.serverlessworkflow.fluent.agentic.configurer.SwitchCaseConfigurer; +import io.serverlessworkflow.fluent.func.FuncCallTaskBuilder; +import io.serverlessworkflow.fluent.func.FuncEmitTaskBuilder; +import io.serverlessworkflow.fluent.func.FuncSwitchTaskBuilder; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; + +public final class AgenticDSL { + + private AgenticDSL() {} + + public static Consumer fn( + Function function, Class argClass) { + return f -> f.function(function, argClass); + } + + public static Consumer fn(Function function) { + return f -> f.function(function); + } + + public static Consumer cases(SwitchCaseConfigurer... cases) { + return s -> { + for (SwitchCaseConfigurer c : cases) { + s.onPredicate(c); + } + }; + } + + public static SwitchCaseSpec on(Predicate when, Class whenClass) { + return new SwitchCaseSpec().when(when, whenClass); + } + + public static SwitchCaseSpec on(Predicate when) { + return new SwitchCaseSpec().when(when); + } + + public static SwitchCaseConfigurer onDefault(String task) { + return s -> s.then(task); + } + + public static SwitchCaseConfigurer onDefault(FlowDirectiveEnum directive) { + return s -> s.then(directive); + } + + public static ListenSpec to() { + return new ListenSpec(); + } + + public static ListenSpec toOne(String type) { + return new ListenSpec().one(e -> e.type(type)); + } + + public static ListenSpec toAll(String... types) { + FuncPredicateEventConfigurer[] events = new FuncPredicateEventConfigurer[types.length]; + for (int i = 0; i < types.length; i++) { + events[i] = event(types[i]); + } + return new ListenSpec().all(events); + } + + public static ListenSpec toAny(String... types) { + FuncPredicateEventConfigurer[] events = new FuncPredicateEventConfigurer[types.length]; + for (int i = 0; i < types.length; i++) { + events[i] = event(types[i]); + } + return new ListenSpec().any(events); + } + + public static FuncPredicateEventConfigurer event(String type) { + return e -> e.type(type); + } + + // TODO: expand the `event` static ref with more attributes based on community feedback + + public static Consumer event( + String type, Function function) { + return event -> event.event(e -> e.type(type).data(function)); + } + + public static Consumer event( + String type, Function function, Class clazz) { + return event -> event.event(e -> e.type(type).data(function, clazz)); + } + + // -------- Agentic Workflow Patterns -------- // + public static AgentTaskConfigurer sequence(Object... agents) { + return list -> list.sequence(agents); + } + + public static AgentTaskConfigurer loop(Predicate exitCondition, Object... agents) { + return list -> list.loop(l -> l.subAgents(agents).exitCondition(exitCondition)); + } + + public static AgentTaskConfigurer parallel(Object... agents) { + return list -> list.parallel(agents); + } + + // --------- Tasks ------ // + public static Consumer doTasks(AgentTaskConfigurer... steps) { + Objects.requireNonNull(steps, "Steps in a tasks are required"); + final List snapshot = List.of(steps.clone()); + return list -> snapshot.forEach(s -> s.accept(list)); + } + + public static AgentTaskConfigurer function(Function function, Class argClass) { + return list -> list.callFn(fn(function, argClass)); + } + + public static AgentTaskConfigurer function(Function function) { + return list -> list.callFn(fn(function)); + } + + public static AgentTaskConfigurer agent(Object agent) { + return list -> list.agent(agent); + } + + public static AgentTaskConfigurer emit(Consumer event) { + return list -> list.emit(event); + } + + public static AgentTaskConfigurer switchCase(Consumer switchCase) { + return list -> list.switchCase(switchCase); + } +} diff --git a/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/dsl/ListenSpec.java b/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/dsl/ListenSpec.java new file mode 100644 index 00000000..7e890ae4 --- /dev/null +++ b/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/dsl/ListenSpec.java @@ -0,0 +1,72 @@ +/* + * 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.fluent.agentic.dsl; + +import io.serverlessworkflow.fluent.agentic.AgentListenTaskBuilder; +import io.serverlessworkflow.fluent.agentic.configurer.FuncPredicateEventConfigurer; +import io.serverlessworkflow.fluent.agentic.configurer.ListenConfigurer; +import io.serverlessworkflow.fluent.func.FuncEventFilterBuilder; +import io.serverlessworkflow.fluent.func.FuncListenToBuilder; +import java.util.Objects; +import java.util.function.Consumer; + +public class ListenSpec implements ListenConfigurer { + + private Consumer strategyStep; + private Consumer untilStep; + + @SuppressWarnings("unchecked") + private static Consumer[] asFilters( + FuncPredicateEventConfigurer[] events) { + Consumer[] filters = new Consumer[events.length]; + for (int i = 0; i < events.length; i++) { + FuncPredicateEventConfigurer ev = Objects.requireNonNull(events[i], "events[" + i + "]"); + filters[i] = f -> f.with(ev); + } + return filters; + } + + public final ListenSpec all(FuncPredicateEventConfigurer... events) { + strategyStep = t -> t.all(asFilters(events)); + return this; + } + + public ListenSpec one(FuncPredicateEventConfigurer e) { + strategyStep = t -> t.one(f -> f.with(e)); + return this; + } + + public final ListenSpec any(FuncPredicateEventConfigurer... events) { + strategyStep = t -> t.any(asFilters(events)); + return this; + } + + public ListenSpec until(String expression) { + untilStep = t -> t.until(expression); + return this; + } + + @Override + public void accept(AgentListenTaskBuilder agentListenTaskBuilder) { + agentListenTaskBuilder.to( + t -> { + strategyStep.accept(t); + if (untilStep != null) { + untilStep.accept(t); + } + }); + } +} diff --git a/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/dsl/SwitchCaseSpec.java b/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/dsl/SwitchCaseSpec.java new file mode 100644 index 00000000..171d722e --- /dev/null +++ b/experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/dsl/SwitchCaseSpec.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.fluent.agentic.dsl; + +import io.serverlessworkflow.fluent.agentic.configurer.SwitchCaseConfigurer; +import io.serverlessworkflow.fluent.func.FuncSwitchTaskBuilder; +import java.util.function.Predicate; + +public class SwitchCaseSpec implements SwitchCaseConfigurer { + + private String then = ""; + private Predicate when; + private Class whenClass; + + public SwitchCaseSpec when(Predicate when, Class whenClass) { + this.when = when; + this.whenClass = whenClass; + return this; + } + + public SwitchCaseSpec when(Predicate when) { + this.when = when; + return this; + } + + public SwitchCaseSpec then(String directive) { + this.then = directive; + return this; + } + + @Override + public void accept(FuncSwitchTaskBuilder.SwitchCasePredicateBuilder switchCasePredicateBuilder) { + if (this.whenClass != null) { + switchCasePredicateBuilder.then(this.then).when(this.when, this.whenClass); + } else { + switchCasePredicateBuilder.then(this.then).when(this.when); + } + } +} diff --git a/experimental/fluent/agentic/src/test/java/io/serverlessworkflow/fluent/agentic/AgentDslWorkflowTest.java b/experimental/fluent/agentic/src/test/java/io/serverlessworkflow/fluent/agentic/AgentDslWorkflowTest.java index 84a348b6..634d0049 100644 --- a/experimental/fluent/agentic/src/test/java/io/serverlessworkflow/fluent/agentic/AgentDslWorkflowTest.java +++ b/experimental/fluent/agentic/src/test/java/io/serverlessworkflow/fluent/agentic/AgentDslWorkflowTest.java @@ -15,6 +15,7 @@ */ package io.serverlessworkflow.fluent.agentic; +import static io.serverlessworkflow.fluent.agentic.AgentWorkflowBuilder.workflow; import static org.assertj.core.api.Assertions.assertThat; import io.serverlessworkflow.api.types.TaskItem; @@ -34,11 +35,24 @@ void dslSequentialAgents() { var a2 = AgentsUtils.newMovieExpert(); var a3 = AgentsUtils.newMovieExpert(); - Workflow wf = - AgentWorkflowBuilder.workflow("seqFlow") - .tasks(tasks -> tasks.sequence("process", a1, a2, a3)) - .build(); + Workflow wf = workflow("seqFlow").tasks(tasks -> tasks.sequence("process", a1, a2, a3)).build(); + this.assertSequentialAgents(wf); + } + + @Test + @DisplayName("Sequential agents via DSL.sequence(...)") + void dslSequentialAgentsShortcut() { + var a1 = AgentsUtils.newMovieExpert(); + var a2 = AgentsUtils.newMovieExpert(); + var a3 = AgentsUtils.newMovieExpert(); + + Workflow wf = workflow("seqFlow").sequence("process", a1, a2, a3).build(); + + this.assertSequentialAgents(wf); + } + + private void assertSequentialAgents(Workflow wf) { List items = wf.getDo(); assertThat(items).hasSize(3); // names should be process-0, process-1, process-2 @@ -53,7 +67,7 @@ void dslSequentialAgents() { @DisplayName("Bare Java‑bean call via DSL.callFn(...)") void dslCallFnBare() { Workflow wf = - AgentWorkflowBuilder.workflow("beanCall") + workflow("beanCall") .tasks(tasks -> tasks.callFn("plainCall", fn -> fn.function(ctx -> "pong"))) .build(); @@ -71,14 +85,7 @@ void dslLoopAgents() { Workflow wf = AgentWorkflowBuilder.workflow("retryFlow") - .tasks( - tasks -> - tasks.loop( - "reviewLoop", - loop -> - loop.maxIterations(5) - .exitCondition(c -> c.readState("score", 0).doubleValue() > 0.75) - .subAgents("reviewer", scorer, editor))) + .loop("reviewLoop", c -> c.readState("score", 0).doubleValue() > 0.75, scorer, editor) .build(); List items = wf.getDo(); @@ -96,10 +103,7 @@ void dslParallelAgents() { var a1 = AgentsUtils.newMovieExpert(); var a2 = AgentsUtils.newMovieExpert(); - Workflow wf = - AgentWorkflowBuilder.workflow("forkFlow") - .tasks(tasks -> tasks.parallel("fanout", a1, a2)) - .build(); + Workflow wf = workflow("forkFlow").parallel("fanout", a1, a2).build(); List items = wf.getDo(); assertThat(items).hasSize(1); @@ -117,7 +121,7 @@ void dslMixSpecAndAgent() { var agent = AgentsUtils.newMovieExpert(); Workflow wf = - AgentWorkflowBuilder.workflow("mixedFlow") + workflow("mixedFlow") .tasks( tasks -> tasks diff --git a/experimental/fluent/agentic/src/test/java/io/serverlessworkflow/fluent/agentic/EmailDrafterIT.java b/experimental/fluent/agentic/src/test/java/io/serverlessworkflow/fluent/agentic/EmailDrafterIT.java index 1eb91839..35022e27 100644 --- a/experimental/fluent/agentic/src/test/java/io/serverlessworkflow/fluent/agentic/EmailDrafterIT.java +++ b/experimental/fluent/agentic/src/test/java/io/serverlessworkflow/fluent/agentic/EmailDrafterIT.java @@ -15,6 +15,12 @@ */ package io.serverlessworkflow.fluent.agentic; +import static io.serverlessworkflow.fluent.agentic.dsl.AgenticDSL.cases; +import static io.serverlessworkflow.fluent.agentic.dsl.AgenticDSL.event; +import static io.serverlessworkflow.fluent.agentic.dsl.AgenticDSL.fn; +import static io.serverlessworkflow.fluent.agentic.dsl.AgenticDSL.on; +import static io.serverlessworkflow.fluent.agentic.dsl.AgenticDSL.onDefault; +import static io.serverlessworkflow.fluent.agentic.dsl.AgenticDSL.toAny; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.fail; @@ -57,48 +63,32 @@ void email_drafter_agent() { tasks -> tasks .agent("agentEmailDrafter", emailDrafter) - .callFn("parseDraft", c -> c.function(EmailDrafts::parse, String.class)) - .callFn( - "policyCheck", - c -> c.function(EmailPolicies::policyCheck, EmailDraft.class)) + .callFn("parseDraft", fn(EmailDrafts::parse, String.class)) + .callFn("policyCheck", fn(EmailPolicies::policyCheck, EmailDraft.class)) .switchCase( "needsHumanReview?", - s -> - s.onPredicate( - c -> - c.when( - decision -> - !EmailPolicies.Decision.AUTO_SEND.equals( - decision.decision()), - PolicyDecision.class) - .then("requestReview")) - .onDefault("emailFinished")) + cases( + on( + d -> !EmailPolicies.Decision.AUTO_SEND.equals(d.decision()), + PolicyDecision.class) + .then("requestReview"), + onDefault("emailFinished"))) .emit( "requestReview", - emit -> - emit.event( - e -> - e.type("org.acme.email.review.required") - .data( - payload -> - PojoCloudEventData.wrap( - payload, - p -> - JsonUtils.mapper() - .writeValueAsString(payload) - .getBytes()), - PolicyDecision.class))) + event( + "org.acme.email.review.required", + payload -> + PojoCloudEventData.wrap( + payload, + p -> + JsonUtils.mapper() + .writeValueAsString(payload) + .getBytes()), + PolicyDecision.class)) .listen( "waitForReview", - listen -> - listen.to( - e -> - e.any( - any -> any.with(r -> r.type("org.acme.email.approved")), - any -> any.with(r -> r.type("org.acme.email.denied"))))) - .emit( - "emailFinished", - emit -> emit.event(e -> e.type("org.acme.email.finished")))) + toAny("org.acme.email.approved", "org.acme.email.denied")) + .emit("emailFinished", event("org.acme.email.finished", null))) .build(); try (WorkflowApplication app = WorkflowApplication.builder().build()) { diff --git a/experimental/fluent/agentic/src/test/java/io/serverlessworkflow/fluent/agentic/MixedWorkflowIT.java b/experimental/fluent/agentic/src/test/java/io/serverlessworkflow/fluent/agentic/MixedWorkflowIT.java index c551eb8a..067c7d37 100644 --- a/experimental/fluent/agentic/src/test/java/io/serverlessworkflow/fluent/agentic/MixedWorkflowIT.java +++ b/experimental/fluent/agentic/src/test/java/io/serverlessworkflow/fluent/agentic/MixedWorkflowIT.java @@ -15,6 +15,9 @@ */ package io.serverlessworkflow.fluent.agentic; +import static io.serverlessworkflow.fluent.agentic.dsl.AgenticDSL.agent; +import static io.serverlessworkflow.fluent.agentic.dsl.AgenticDSL.doTasks; +import static io.serverlessworkflow.fluent.agentic.dsl.AgenticDSL.function; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.spy; @@ -60,28 +63,14 @@ void mixed_workflow() { final Workflow mixedWorkflow = AgentWorkflowBuilder.workflow("chat-bot") .tasks( - t -> - t.callFn( - callJ -> - callJ.function( - input -> { - System.out.println(input); - return Map.of("userInput", input); - }, - String.class)) - .agent(chatBot) - .callFn( - callJ -> - callJ.function( - input -> { - System.out.println(input); - // Here, we are return a simple string so the internal - // AgenticScope will add it to the default `input` key - // If we want to really manipulate it, we could return a - // Map<>(message, input) - return "I've changed the input [" + input + "]"; - }, - String.class))) + doTasks( + function(input -> Map.of("userInput", input), String.class), + agent(chatBot), + // Here, we are return a simple string so the internal + // AgenticScope will add it to the default `input` key + // If we want to really manipulate it, we could return a + // Map<>(message, input) + function(input -> "I've changed the input [" + input + "]", String.class))) .build(); try (WorkflowApplication app = WorkflowApplication.builder().build()) { diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncSwitchTaskBuilder.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncSwitchTaskBuilder.java index f0294861..c4ee2b09 100644 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncSwitchTaskBuilder.java +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncSwitchTaskBuilder.java @@ -27,6 +27,7 @@ import io.serverlessworkflow.fluent.spec.spi.SwitchTaskFluent; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.UUID; import java.util.function.Consumer; import java.util.function.Predicate; @@ -58,6 +59,18 @@ public FuncSwitchTaskBuilder onPredicate( String name, Consumer consumer) { final SwitchCasePredicateBuilder switchCase = new SwitchCasePredicateBuilder(); consumer.accept(switchCase); + final SwitchCaseFunction switchCaseValue = (SwitchCaseFunction) switchCase.build(); + + // Handling default cases + if (switchCaseValue.predicate() == null) { + Objects.requireNonNull(switchCaseValue.getThen(), "When is required"); + if (switchCaseValue.getThen().getFlowDirectiveEnum() != null) { + return this.onDefault(switchCaseValue.getThen().getFlowDirectiveEnum()); + } else { + return this.onDefault(switchCaseValue.getThen().getString()); + } + } + this.switchItems.add(new SwitchItem(name, switchCase.build())); return this; } diff --git a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseWorkflowBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseWorkflowBuilder.java index 3aed00ad..c3127a30 100644 --- a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseWorkflowBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseWorkflowBuilder.java @@ -34,7 +34,7 @@ public abstract class BaseWorkflowBuilder< public static final String DEFAULT_VERSION = "0.0.1"; public static final String DEFAULT_NAMESPACE = "org.acme"; - private final Workflow workflow; + protected final Workflow workflow; private final Document document; protected BaseWorkflowBuilder(final String name, final String namespace, final String version) { @@ -87,7 +87,11 @@ public SELF use(Consumer useBuilderConsumer) { public SELF tasks(Consumer doTaskConsumer) { final DBuilder doTaskBuilder = newDo(); doTaskConsumer.accept(doTaskBuilder); - this.workflow.setDo(doTaskBuilder.build().getDo()); + if (this.workflow.getDo() == null) { + this.workflow.setDo(doTaskBuilder.build().getDo()); + } else { + this.workflow.getDo().addAll(doTaskBuilder.build().getDo()); + } return self(); }