Skip to content

Commit 28d5880

Browse files
committed
Adding agentic workflow patterns to the DSL
Signed-off-by: Ricardo Zanini <[email protected]>
1 parent 51d6af6 commit 28d5880

File tree

4 files changed

+78
-20
lines changed

4 files changed

+78
-20
lines changed

experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/AgentWorkflowBuilder.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
*/
1616
package io.serverlessworkflow.fluent.agentic;
1717

18+
import dev.langchain4j.agentic.scope.AgenticScope;
1819
import io.serverlessworkflow.fluent.func.spi.FuncTransformations;
1920
import io.serverlessworkflow.fluent.spec.BaseWorkflowBuilder;
2021
import java.util.UUID;
22+
import java.util.function.Predicate;
2123

2224
public class AgentWorkflowBuilder
2325
extends BaseWorkflowBuilder<AgentWorkflowBuilder, AgentDoTaskBuilder, AgentTaskItemListBuilder>
@@ -40,6 +42,40 @@ public static AgentWorkflowBuilder workflow(String name, String ns) {
4042
return new AgentWorkflowBuilder(name, ns, DEFAULT_VERSION);
4143
}
4244

45+
public AgentWorkflowBuilder sequence(Object... agents) {
46+
return sequence(UUID.randomUUID().toString(), agents);
47+
}
48+
49+
public AgentWorkflowBuilder sequence(String name, Object... agents) {
50+
final AgentDoTaskBuilder doTaskBuilder = this.newDo();
51+
doTaskBuilder.sequence(name, agents);
52+
this.workflow.setDo(doTaskBuilder.build().getDo());
53+
return this;
54+
}
55+
56+
public AgentWorkflowBuilder parallel(Object... agents) {
57+
return this.parallel(UUID.randomUUID().toString(), agents);
58+
}
59+
60+
public AgentWorkflowBuilder parallel(String name, Object... agents) {
61+
final AgentDoTaskBuilder doTaskBuilder = this.newDo();
62+
doTaskBuilder.parallel(name, agents);
63+
this.workflow.setDo(doTaskBuilder.build().getDo());
64+
return this;
65+
}
66+
67+
public AgentWorkflowBuilder loop(Predicate<AgenticScope> exitCondition, Object... agents) {
68+
return this.loop(UUID.randomUUID().toString(), exitCondition, agents);
69+
}
70+
71+
public AgentWorkflowBuilder loop(
72+
String name, Predicate<AgenticScope> exitCondition, Object... agents) {
73+
final AgentDoTaskBuilder doTaskBuilder = this.newDo();
74+
doTaskBuilder.loop(name, loop -> loop.subAgents(agents).exitCondition(exitCondition));
75+
this.workflow.setDo(doTaskBuilder.build().getDo());
76+
return this;
77+
}
78+
4379
@Override
4480
protected AgentDoTaskBuilder newDo() {
4581
return new AgentDoTaskBuilder();

experimental/fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/dsl/AgenticDSL.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package io.serverlessworkflow.fluent.agentic.dsl;
1717

18+
import dev.langchain4j.agentic.scope.AgenticScope;
1819
import io.cloudevents.CloudEventData;
1920
import io.serverlessworkflow.api.types.FlowDirectiveEnum;
2021
import io.serverlessworkflow.fluent.agentic.AgentDoTaskBuilder;
@@ -107,6 +108,19 @@ public static <T> Consumer<FuncEmitTaskBuilder> event(
107108
return event -> event.event(e -> e.type(type).data(function, clazz));
108109
}
109110

111+
// -------- Agentic Workflow Patterns -------- //
112+
public static AgentTaskConfigurer sequence(Object... agents) {
113+
return list -> list.sequence(agents);
114+
}
115+
116+
public static AgentTaskConfigurer loop(Predicate<AgenticScope> exitCondition, Object... agents) {
117+
return list -> list.loop(l -> l.subAgents(agents).exitCondition(exitCondition));
118+
}
119+
120+
public static AgentTaskConfigurer parallel(Object... agents) {
121+
return list -> list.parallel(agents);
122+
}
123+
110124
// --------- Tasks ------ //
111125
public static Consumer<AgentDoTaskBuilder> doTasks(AgentTaskConfigurer... steps) {
112126
Objects.requireNonNull(steps, "Steps in a tasks are required");

experimental/fluent/agentic/src/test/java/io/serverlessworkflow/fluent/agentic/AgentDslWorkflowTest.java

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package io.serverlessworkflow.fluent.agentic;
1717

18+
import static io.serverlessworkflow.fluent.agentic.AgentWorkflowBuilder.workflow;
1819
import static org.assertj.core.api.Assertions.assertThat;
1920

2021
import io.serverlessworkflow.api.types.TaskItem;
@@ -34,11 +35,24 @@ void dslSequentialAgents() {
3435
var a2 = AgentsUtils.newMovieExpert();
3536
var a3 = AgentsUtils.newMovieExpert();
3637

37-
Workflow wf =
38-
AgentWorkflowBuilder.workflow("seqFlow")
39-
.tasks(tasks -> tasks.sequence("process", a1, a2, a3))
40-
.build();
38+
Workflow wf = workflow("seqFlow").tasks(tasks -> tasks.sequence("process", a1, a2, a3)).build();
4139

40+
this.assertSequentialAgents(wf);
41+
}
42+
43+
@Test
44+
@DisplayName("Sequential agents via DSL.sequence(...)")
45+
void dslSequentialAgentsShortcut() {
46+
var a1 = AgentsUtils.newMovieExpert();
47+
var a2 = AgentsUtils.newMovieExpert();
48+
var a3 = AgentsUtils.newMovieExpert();
49+
50+
Workflow wf = workflow("seqFlow").sequence("process", a1, a2, a3).build();
51+
52+
this.assertSequentialAgents(wf);
53+
}
54+
55+
private void assertSequentialAgents(Workflow wf) {
4256
List<TaskItem> items = wf.getDo();
4357
assertThat(items).hasSize(3);
4458
// names should be process-0, process-1, process-2
@@ -53,7 +67,7 @@ void dslSequentialAgents() {
5367
@DisplayName("Bare Java‑bean call via DSL.callFn(...)")
5468
void dslCallFnBare() {
5569
Workflow wf =
56-
AgentWorkflowBuilder.workflow("beanCall")
70+
workflow("beanCall")
5771
.tasks(tasks -> tasks.callFn("plainCall", fn -> fn.function(ctx -> "pong")))
5872
.build();
5973

@@ -71,14 +85,7 @@ void dslLoopAgents() {
7185

7286
Workflow wf =
7387
AgentWorkflowBuilder.workflow("retryFlow")
74-
.tasks(
75-
tasks ->
76-
tasks.loop(
77-
"reviewLoop",
78-
loop ->
79-
loop.maxIterations(5)
80-
.exitCondition(c -> c.readState("score", 0).doubleValue() > 0.75)
81-
.subAgents("reviewer", scorer, editor)))
88+
.loop("reviewLoop", c -> c.readState("score", 0).doubleValue() > 0.75, scorer, editor)
8289
.build();
8390

8491
List<TaskItem> items = wf.getDo();
@@ -96,10 +103,7 @@ void dslParallelAgents() {
96103
var a1 = AgentsUtils.newMovieExpert();
97104
var a2 = AgentsUtils.newMovieExpert();
98105

99-
Workflow wf =
100-
AgentWorkflowBuilder.workflow("forkFlow")
101-
.tasks(tasks -> tasks.parallel("fanout", a1, a2))
102-
.build();
106+
Workflow wf = workflow("forkFlow").parallel("fanout", a1, a2).build();
103107

104108
List<TaskItem> items = wf.getDo();
105109
assertThat(items).hasSize(1);
@@ -117,7 +121,7 @@ void dslMixSpecAndAgent() {
117121
var agent = AgentsUtils.newMovieExpert();
118122

119123
Workflow wf =
120-
AgentWorkflowBuilder.workflow("mixedFlow")
124+
workflow("mixedFlow")
121125
.tasks(
122126
tasks ->
123127
tasks

fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseWorkflowBuilder.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public abstract class BaseWorkflowBuilder<
3434
public static final String DEFAULT_VERSION = "0.0.1";
3535
public static final String DEFAULT_NAMESPACE = "org.acme";
3636

37-
private final Workflow workflow;
37+
protected final Workflow workflow;
3838
private final Document document;
3939

4040
protected BaseWorkflowBuilder(final String name, final String namespace, final String version) {
@@ -87,7 +87,11 @@ public SELF use(Consumer<UseBuilder> useBuilderConsumer) {
8787
public SELF tasks(Consumer<DBuilder> doTaskConsumer) {
8888
final DBuilder doTaskBuilder = newDo();
8989
doTaskConsumer.accept(doTaskBuilder);
90-
this.workflow.setDo(doTaskBuilder.build().getDo());
90+
if (this.workflow.getDo() == null) {
91+
this.workflow.setDo(doTaskBuilder.build().getDo());
92+
} else {
93+
this.workflow.getDo().addAll(doTaskBuilder.build().getDo());
94+
}
9195
return self();
9296
}
9397

0 commit comments

Comments
 (0)