Skip to content

Commit 486ba33

Browse files
committed
more examples
Signed-off-by: Dmitrii Tikhomirov <[email protected]>
1 parent ee7b32b commit 486ba33

File tree

3 files changed

+179
-3
lines changed

3 files changed

+179
-3
lines changed

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

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

18+
import dev.langchain4j.agent.tool.Tool;
1819
import dev.langchain4j.agentic.Agent;
1920
import dev.langchain4j.agentic.internal.AgentSpecification;
2021
import dev.langchain4j.service.SystemMessage;
@@ -318,4 +319,60 @@ interface AstrologyAgent {
318319
@Agent("An astrologist that generates horoscopes based on the user's name and zodiac sign.")
319320
String horoscope(@V("name") String name, @V("sign") String sign);
320321
}
322+
323+
enum RequestCategory {
324+
LEGAL,
325+
MEDICAL,
326+
TECHNICAL,
327+
UNKNOWN
328+
}
329+
330+
interface CategoryRouter {
331+
332+
@UserMessage(
333+
"""
334+
Analyze the following user request and categorize it as 'legal', 'medical' or 'technical'.
335+
In case the request doesn't belong to any of those categories categorize it as 'unknown'.
336+
Reply with only one of those words and nothing else.
337+
The user request is: '{{request}}'.
338+
""")
339+
@Agent("Categorizes a user request")
340+
RequestCategory classify(@V("request") String request);
341+
}
342+
343+
interface MedicalExpert {
344+
345+
@dev.langchain4j.service.UserMessage(
346+
"""
347+
You are a medical expert.
348+
Analyze the following user request under a medical point of view and provide the best possible answer.
349+
The user request is {{it}}.
350+
""")
351+
@Tool("A medical expert")
352+
String medicalRequest(String request);
353+
}
354+
355+
interface LegalExpert {
356+
357+
@dev.langchain4j.service.UserMessage(
358+
"""
359+
You are a legal expert.
360+
Analyze the following user request under a legal point of view and provide the best possible answer.
361+
The user request is {{it}}.
362+
""")
363+
@Tool("A legal expert")
364+
String legalRequest(String request);
365+
}
366+
367+
interface TechnicalExpert {
368+
369+
@dev.langchain4j.service.UserMessage(
370+
"""
371+
You are a technical expert.
372+
Analyze the following user request under a technical point of view and provide the best possible answer.
373+
The user request is {{it}}.
374+
""")
375+
@Tool("A technical expert")
376+
String technicalRequest(String request);
377+
}
321378
}

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,36 @@ public static Agents.AstrologyAgent newAstrologyAgent() {
7979
.outputName("horoscope")
8080
.build());
8181
}
82+
83+
public static Agents.CategoryRouter newCategoryRouter() {
84+
return spy(
85+
AgenticServices.agentBuilder(Agents.CategoryRouter.class)
86+
.chatModel(BASE_MODEL)
87+
.outputName("category")
88+
.build());
89+
}
90+
91+
public static Agents.MedicalExpert newMedicalExpert() {
92+
return spy(
93+
AgenticServices.agentBuilder(Agents.MedicalExpert.class)
94+
.chatModel(BASE_MODEL)
95+
.outputName("response")
96+
.build());
97+
}
98+
99+
public static Agents.TechnicalExpert newTechnicalExpert() {
100+
return spy(
101+
AgenticServices.agentBuilder(Agents.TechnicalExpert.class)
102+
.chatModel(BASE_MODEL)
103+
.outputName("response")
104+
.build());
105+
}
106+
107+
public static Agents.LegalExpert newLegalExpert() {
108+
return spy(
109+
AgenticServices.agentBuilder(Agents.LegalExpert.class)
110+
.chatModel(BASE_MODEL)
111+
.outputName("response")
112+
.build());
113+
}
82114
}

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

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public void sequentialWorkflow() {
6868
}
6969

7070
@Test
71-
@DisplayName("Looping agents via DSL.loop(...)") // TODO maxIterations(5)
71+
@DisplayName("Looping agents via DSL.loop(...)")
7272
public void loopWorkflow() {
7373

7474
var scorer = AgentsUtils.newStyleScorer();
@@ -103,6 +103,48 @@ public void loopWorkflow() {
103103
assertThat(result).containsKey("story");
104104
}
105105

106+
@Test
107+
@DisplayName("Looping agents via DSL.loop(...)")
108+
public void loopWorkflowWithMaxIterations() {
109+
var scorer = AgentsUtils.newStyleScorer();
110+
var editor = AgentsUtils.newStyleEditor();
111+
112+
Workflow wf =
113+
AgentWorkflowBuilder.workflow("maxFlow")
114+
.tasks(
115+
d ->
116+
d.loop(
117+
"limit",
118+
l ->
119+
l.maxIterations(5)
120+
.exitCondition(c -> c.readState("score", 0).doubleValue() >= 0.8)
121+
.subAgents("sub", scorer, editor)))
122+
.build();
123+
124+
List<TaskItem> items = wf.getDo();
125+
assertThat(items).hasSize(1);
126+
127+
var fn = (ForTaskFunction) items.get(0).getTask().getForTask();
128+
assertThat(fn.getDo()).isNotNull();
129+
assertThat(fn.getDo()).hasSize(2);
130+
fn.getDo()
131+
.forEach(si -> assertThat(si.getTask().getCallTask()).isInstanceOf(CallTaskJava.class));
132+
133+
Map<String, Object> input =
134+
Map.of(
135+
"story", "dragons and wizards",
136+
"style", "comedy");
137+
138+
Map<String, Object> result;
139+
try (WorkflowApplication app = WorkflowApplication.builder().build()) {
140+
result = app.workflowDefinition(wf).instance(input).start().get().asMap().orElseThrow();
141+
} catch (Exception e) {
142+
throw new RuntimeException("Workflow execution failed", e);
143+
}
144+
145+
assertThat(result).containsKey("story");
146+
}
147+
106148
@Test
107149
@DisplayName("Parallel agents via DSL.parallel(...)")
108150
public void parallelWorkflow() {
@@ -115,7 +157,9 @@ public void parallelWorkflow() {
115157
assertThat(items).hasSize(1);
116158

117159
var fork = items.get(0).getTask().getForkTask();
160+
// two branches created
118161
assertThat(fork.getFork().getBranches()).hasSize(2);
162+
// branch names follow "branch-{index}-{name}"
119163
assertThat(fork.getFork().getBranches().get(0).getName()).isEqualTo("branch-0-fanout");
120164
assertThat(fork.getFork().getBranches().get(1).getName()).isEqualTo("branch-1-fanout");
121165

@@ -132,6 +176,50 @@ public void parallelWorkflow() {
132176
assertEquals("Fake conflict response", result.get("movies"));
133177
}
134178

179+
// TODO
180+
@Test
181+
@DisplayName("Conditional agents via choice(...)")
182+
public void conditionalWorkflow() {
183+
184+
var category = AgentsUtils.newCategoryRouter();
185+
var a1 = AgentsUtils.newMedicalExpert();
186+
var a2 = AgentsUtils.newTechnicalExpert();
187+
var a3 = AgentsUtils.newLegalExpert();
188+
}
189+
190+
// TODO
191+
@Test
192+
@DisplayName("Error handling with agents")
193+
public void errorHandling() {
194+
var a1 = AgentsUtils.newCreativeWriter();
195+
var a2 = AgentsUtils.newAudienceEditor();
196+
var a3 = AgentsUtils.newStyleEditor();
197+
198+
Workflow wf = workflow("seqFlow").tasks(tasks -> tasks.sequence("process", a1, a2, a3)).build();
199+
200+
List<TaskItem> items = wf.getDo();
201+
assertThat(items).hasSize(3);
202+
203+
assertThat(items.get(0).getName()).isEqualTo("process-0");
204+
assertThat(items.get(1).getName()).isEqualTo("process-1");
205+
assertThat(items.get(2).getName()).isEqualTo("process-2");
206+
items.forEach(it -> assertThat(it.getTask().getCallTask()).isInstanceOf(CallTaskJava.class));
207+
208+
Map<String, Object> input =
209+
Map.of(
210+
"style", "fantasy",
211+
"audience", "young adults");
212+
213+
Map<String, Object> result;
214+
try (WorkflowApplication app = WorkflowApplication.builder().build()) {
215+
result = app.workflowDefinition(wf).instance(input).start().get().asMap().orElseThrow();
216+
} catch (Exception e) {
217+
throw new RuntimeException("Workflow execution failed", e);
218+
}
219+
220+
assertThat(result).containsKey("story");
221+
}
222+
135223
@Test
136224
@DisplayName("Human in the loop")
137225
public void humanInTheLoop() {
@@ -149,8 +237,7 @@ public void humanInTheLoop() {
149237

150238
var a1 = AgentsUtils.newAstrologyAgent();
151239

152-
Workflow wf =
153-
workflow("seqFlow").tasks(tasks -> tasks.sequence("process", a1, humanInTheLoop)).build();
240+
Workflow wf = workflow("seqFlow").sequence("process", a1, humanInTheLoop).build();
154241

155242
assertThat(wf.getDo()).hasSize(2);
156243

0 commit comments

Comments
 (0)