Skip to content

Commit 564db09

Browse files
committed
Introduce ChatBot example
Signed-off-by: Ricardo Zanini <[email protected]>
1 parent 86bef90 commit 564db09

File tree

12 files changed

+154
-10
lines changed

12 files changed

+154
-10
lines changed

experimental/agentic/src/main/java/io/serverlessworkflow/impl/expressions/agentic/AgenticModelFactory.java

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,19 @@
2828

2929
class AgenticModelFactory implements WorkflowModelFactory {
3030

31+
private static final String DEFAULT_AGENTIC_SCOPE_STATE_KEY = "input";
32+
private final AgenticScopeRegistryAssessor scopeRegistryAssessor =
33+
new AgenticScopeRegistryAssessor();
34+
35+
private AgenticModel asAgenticModel(Object value) {
36+
// TODO: fetch memoryId from the object based on known premises
37+
final AgenticScope agenticScope = this.scopeRegistryAssessor.getAgenticScope();
38+
if (value != null) {
39+
agenticScope.writeState(DEFAULT_AGENTIC_SCOPE_STATE_KEY, value);
40+
}
41+
return new AgenticModel(agenticScope);
42+
}
43+
3144
/**
3245
* Applies any change to the model after running as task. We will always set it to a @AgenticScope
3346
* object since @AgentExecutor is always adding the output to the agenticScope. We just have to
@@ -60,51 +73,53 @@ public WorkflowModelCollection createCollection() {
6073

6174
@Override
6275
public WorkflowModel from(boolean value) {
63-
return new JavaModel(value);
76+
return asAgenticModel(value);
6477
}
6578

6679
@Override
6780
public WorkflowModel from(Number value) {
68-
return new JavaModel(value);
81+
return asAgenticModel(value);
6982
}
7083

7184
@Override
7285
public WorkflowModel from(String value) {
73-
return new JavaModel(value);
86+
return asAgenticModel(value);
7487
}
7588

7689
@Override
7790
public WorkflowModel from(CloudEvent ce) {
91+
// TODO: serialize the CE into the AgenticScope
7892
return new JavaModel(ce);
7993
}
8094

8195
@Override
8296
public WorkflowModel from(CloudEventData ce) {
97+
// TODO: serialize the CE data into the AgenticScope
8398
return new JavaModel(ce);
8499
}
85100

86101
@Override
87102
public WorkflowModel from(OffsetDateTime value) {
88-
return new JavaModel(value);
103+
return asAgenticModel(value);
89104
}
90105

91106
@Override
92107
public WorkflowModel from(Map<String, Object> map) {
93-
final AgenticScope agenticScope = new AgenticScopeRegistryAssessor().getAgenticScope();
108+
final AgenticScope agenticScope = this.scopeRegistryAssessor.getAgenticScope();
94109
agenticScope.writeStates(map);
95110
return new AgenticModel(agenticScope);
96111
}
97112

98113
@Override
99114
public WorkflowModel fromNull() {
100-
return new JavaModel(null);
115+
return asAgenticModel(null);
101116
}
102117

103118
@Override
104119
public WorkflowModel fromOther(Object value) {
105120
if (value instanceof AgenticScope) {
106121
return new AgenticModel((AgenticScope) value);
107122
}
108-
return new JavaModel(value);
123+
return asAgenticModel(value);
109124
}
110125
}

fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/AgentDoTaskBuilder.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import io.serverlessworkflow.fluent.func.FuncEmitTaskBuilder;
2121
import io.serverlessworkflow.fluent.func.FuncForTaskBuilder;
2222
import io.serverlessworkflow.fluent.func.FuncForkTaskBuilder;
23+
import io.serverlessworkflow.fluent.func.FuncListenTaskBuilder;
2324
import io.serverlessworkflow.fluent.func.FuncSetTaskBuilder;
2425
import io.serverlessworkflow.fluent.func.FuncSwitchTaskBuilder;
2526
import io.serverlessworkflow.fluent.func.spi.ConditionalTaskBuilder;
@@ -81,6 +82,12 @@ public AgentDoTaskBuilder emit(String name, Consumer<FuncEmitTaskBuilder> itemsC
8182
return self();
8283
}
8384

85+
@Override
86+
public AgentDoTaskBuilder listen(String name, Consumer<FuncListenTaskBuilder> itemsConfigurer) {
87+
this.listBuilder().listen(name, itemsConfigurer);
88+
return self();
89+
}
90+
8491
@Override
8592
public AgentDoTaskBuilder forEach(String name, Consumer<FuncForTaskBuilder> itemsConfigurer) {
8693
this.listBuilder().forEach(name, itemsConfigurer);

fluent/agentic/src/main/java/io/serverlessworkflow/fluent/agentic/AgentTaskItemListBuilder.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import io.serverlessworkflow.fluent.func.FuncEmitTaskBuilder;
2525
import io.serverlessworkflow.fluent.func.FuncForTaskBuilder;
2626
import io.serverlessworkflow.fluent.func.FuncForkTaskBuilder;
27+
import io.serverlessworkflow.fluent.func.FuncListenTaskBuilder;
2728
import io.serverlessworkflow.fluent.func.FuncSetTaskBuilder;
2829
import io.serverlessworkflow.fluent.func.FuncSwitchTaskBuilder;
2930
import io.serverlessworkflow.fluent.func.FuncTaskItemListBuilder;
@@ -113,6 +114,13 @@ public AgentTaskItemListBuilder emit(String name, Consumer<FuncEmitTaskBuilder>
113114
return self();
114115
}
115116

117+
@Override
118+
public AgentTaskItemListBuilder listen(
119+
String name, Consumer<FuncListenTaskBuilder> itemsConfigurer) {
120+
this.delegate.listen(name, itemsConfigurer);
121+
return self();
122+
}
123+
116124
@Override
117125
public AgentTaskItemListBuilder forEach(
118126
String name, Consumer<FuncForTaskBuilder> itemsConfigurer) {

fluent/agentic/src/test/java/io/serverlessworkflow/fluent/agentic/Agents.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,22 @@
1717

1818
import dev.langchain4j.agentic.Agent;
1919
import dev.langchain4j.agentic.internal.AgentSpecification;
20+
import dev.langchain4j.service.MemoryId;
21+
import dev.langchain4j.service.SystemMessage;
2022
import dev.langchain4j.service.UserMessage;
2123
import dev.langchain4j.service.V;
2224
import java.util.List;
2325

2426
public interface Agents {
2527

28+
@SystemMessage(
29+
"""
30+
You are a happy chat bot.
31+
""")
32+
interface ChatBot {
33+
String chat(@MemoryId String memoryId, @V("message") String message);
34+
}
35+
2636
interface MovieExpert {
2737

2838
@UserMessage(
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2020-Present The Serverless Workflow Specification Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.serverlessworkflow.fluent.agentic;
17+
18+
import static org.mockito.Mockito.spy;
19+
20+
import dev.langchain4j.agentic.AgenticServices;
21+
import dev.langchain4j.agentic.scope.AgenticScope;
22+
import org.junit.jupiter.api.Test;
23+
24+
public class ChatBotIT {
25+
26+
@Test
27+
void chat_bot() {
28+
Agents.ChatBot chatBot =
29+
spy(
30+
AgenticServices.agentBuilder(Agents.ChatBot.class)
31+
.chatModel(Models.BASE_MODEL)
32+
.outputName("message")
33+
.build());
34+
// 1. listen to an event containing `message` key in the body
35+
// 2. if contains, call the agent, if not end the workflow
36+
// 3. After replying to the chat, return
37+
AgentWorkflowBuilder.workflow("chat-bot")
38+
.tasks(
39+
t ->
40+
t.listen(
41+
"listenToMessages",
42+
l ->
43+
l.outputAs(null)
44+
.one(c -> c.with(event -> event.type("org.acme.chatbot"))))
45+
.when(scope -> !"".equals(scope.readState("message")), AgenticScope.class)
46+
.agent(chatBot)
47+
.then("listenToMessages"));
48+
}
49+
}

fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncDoTaskBuilder.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ public FuncDoTaskBuilder emit(String name, Consumer<FuncEmitTaskBuilder> itemsCo
4141
return this;
4242
}
4343

44+
@Override
45+
public FuncDoTaskBuilder listen(String name, Consumer<FuncListenTaskBuilder> itemsConfigurer) {
46+
this.listBuilder().listen(name, itemsConfigurer);
47+
return this;
48+
}
49+
4450
@Override
4551
public FuncDoTaskBuilder forEach(String name, Consumer<FuncForTaskBuilder> itemsConfigurer) {
4652
this.listBuilder().forEach(name, itemsConfigurer);

fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncEmitTaskBuilder.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616
package io.serverlessworkflow.fluent.func;
1717

1818
import io.serverlessworkflow.fluent.func.spi.ConditionalTaskBuilder;
19+
import io.serverlessworkflow.fluent.func.spi.FuncTransformations;
1920
import io.serverlessworkflow.fluent.spec.EmitTaskBuilder;
2021

2122
public class FuncEmitTaskBuilder extends EmitTaskBuilder
22-
implements ConditionalTaskBuilder<FuncSetTaskBuilder> {
23+
implements ConditionalTaskBuilder<FuncEmitTaskBuilder>,
24+
FuncTransformations<FuncEmitTaskBuilder> {
2325
FuncEmitTaskBuilder() {
2426
super();
2527
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2020-Present The Serverless Workflow Specification Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.serverlessworkflow.fluent.func;
17+
18+
import io.serverlessworkflow.fluent.func.spi.ConditionalTaskBuilder;
19+
import io.serverlessworkflow.fluent.func.spi.FuncTransformations;
20+
import io.serverlessworkflow.fluent.spec.ListenTaskBuilder;
21+
22+
public class FuncListenTaskBuilder extends ListenTaskBuilder
23+
implements ConditionalTaskBuilder<FuncListenTaskBuilder>,
24+
FuncTransformations<FuncListenTaskBuilder> {
25+
26+
FuncListenTaskBuilder() {
27+
super();
28+
}
29+
}

fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncTaskItemListBuilder.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,16 @@ public FuncTaskItemListBuilder emit(String name, Consumer<FuncEmitTaskBuilder> i
7979
new TaskItem(name, new Task().withEmitTask(emitTaskJavaBuilder.build())));
8080
}
8181

82+
@Override
83+
public FuncTaskItemListBuilder listen(
84+
String name, Consumer<FuncListenTaskBuilder> itemsConfigurer) {
85+
this.requireNameAndConfig(name, itemsConfigurer);
86+
final FuncListenTaskBuilder listenTaskJavaBuilder = new FuncListenTaskBuilder();
87+
itemsConfigurer.accept(listenTaskJavaBuilder);
88+
return this.addTaskItem(
89+
new TaskItem(name, new Task().withListenTask(listenTaskJavaBuilder.build())));
90+
}
91+
8292
@Override
8393
public FuncTaskItemListBuilder forEach(
8494
String name, Consumer<FuncForTaskBuilder> itemsConfigurer) {

fluent/func/src/main/java/io/serverlessworkflow/fluent/func/spi/FuncDoFluent.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@
1919
import io.serverlessworkflow.fluent.func.FuncEmitTaskBuilder;
2020
import io.serverlessworkflow.fluent.func.FuncForTaskBuilder;
2121
import io.serverlessworkflow.fluent.func.FuncForkTaskBuilder;
22+
import io.serverlessworkflow.fluent.func.FuncListenTaskBuilder;
2223
import io.serverlessworkflow.fluent.func.FuncSetTaskBuilder;
2324
import io.serverlessworkflow.fluent.func.FuncSwitchTaskBuilder;
2425
import io.serverlessworkflow.fluent.spec.spi.EmitFluent;
2526
import io.serverlessworkflow.fluent.spec.spi.ForEachFluent;
2627
import io.serverlessworkflow.fluent.spec.spi.ForkFluent;
28+
import io.serverlessworkflow.fluent.spec.spi.ListenFluent;
2729
import io.serverlessworkflow.fluent.spec.spi.SetFluent;
2830
import io.serverlessworkflow.fluent.spec.spi.SwitchFluent;
2931
import java.util.UUID;
@@ -36,7 +38,8 @@ public interface FuncDoFluent<SELF extends FuncDoFluent<SELF>>
3638
EmitFluent<FuncEmitTaskBuilder, SELF>,
3739
ForEachFluent<FuncForTaskBuilder, SELF>,
3840
SwitchFluent<FuncSwitchTaskBuilder, SELF>,
39-
ForkFluent<FuncForkTaskBuilder, SELF> {
41+
ForkFluent<FuncForkTaskBuilder, SELF>,
42+
ListenFluent<FuncListenTaskBuilder, SELF> {
4043

4144
SELF callFn(String name, Consumer<FuncCallTaskBuilder> cfg);
4245

0 commit comments

Comments
 (0)