|
15 | 15 | */ |
16 | 16 | package io.serverlessworkflow.fluent.agentic.langchain4j; |
17 | 17 |
|
| 18 | +import static io.serverlessworkflow.fluent.agentic.AgentWorkflowBuilder.workflow; |
| 19 | +import static io.serverlessworkflow.fluent.agentic.AgentsUtils.newAstrologyAgent; |
| 20 | +import static io.serverlessworkflow.fluent.agentic.AgentsUtils.newAudienceEditor; |
| 21 | +import static io.serverlessworkflow.fluent.agentic.AgentsUtils.newCreativeWriter; |
| 22 | +import static io.serverlessworkflow.fluent.agentic.AgentsUtils.newFoodExpert; |
| 23 | +import static io.serverlessworkflow.fluent.agentic.AgentsUtils.newMovieExpert; |
| 24 | +import static io.serverlessworkflow.fluent.agentic.AgentsUtils.newStyleEditor; |
| 25 | +import static io.serverlessworkflow.fluent.agentic.AgentsUtils.newStyleScorer; |
| 26 | +import static io.serverlessworkflow.fluent.agentic.AgentsUtils.newSummaryStory; |
| 27 | +import static io.serverlessworkflow.fluent.agentic.langchain4j.Agents.*; |
18 | 28 | import static io.serverlessworkflow.fluent.agentic.langchain4j.Agents.AudienceEditor; |
19 | 29 | import static io.serverlessworkflow.fluent.agentic.langchain4j.Agents.CreativeWriter; |
20 | 30 | import static io.serverlessworkflow.fluent.agentic.langchain4j.Agents.StyleEditor; |
21 | 31 | import static io.serverlessworkflow.fluent.agentic.langchain4j.Models.BASE_MODEL; |
| 32 | +import static org.junit.jupiter.api.Assertions.assertEquals; |
| 33 | +import static org.junit.jupiter.api.Assertions.assertNotNull; |
22 | 34 | import static org.mockito.ArgumentMatchers.any; |
23 | 35 | import static org.mockito.ArgumentMatchers.eq; |
24 | 36 | import static org.mockito.Mockito.spy; |
25 | 37 | import static org.mockito.Mockito.verify; |
26 | 38 |
|
27 | 39 | import dev.langchain4j.agentic.AgenticServices; |
28 | 40 | import dev.langchain4j.agentic.UntypedAgent; |
| 41 | +import dev.langchain4j.agentic.scope.AgenticScope; |
29 | 42 | import dev.langchain4j.agentic.workflow.WorkflowAgentsBuilder; |
| 43 | +import io.serverlessworkflow.fluent.agentic.AgenticWorkflow; |
| 44 | +import io.serverlessworkflow.fluent.agentic.AgentsUtils; |
| 45 | +import java.util.List; |
30 | 46 | import java.util.Map; |
| 47 | +import java.util.function.Function; |
| 48 | +import java.util.function.Predicate; |
| 49 | +import java.util.stream.IntStream; |
31 | 50 | import org.junit.jupiter.api.Test; |
32 | 51 |
|
33 | 52 | public class WorkflowAgentsIT { |
@@ -77,4 +96,123 @@ void sequential_agents_tests() { |
77 | 96 | verify(audienceEditor).editStory(any(), eq("young adults")); |
78 | 97 | verify(styleEditor).editStory(any(), eq("fantasy")); |
79 | 98 | } |
| 99 | + |
| 100 | + @Test |
| 101 | + public void sequenceHelperTest() { |
| 102 | + var creativeWriter = newCreativeWriter(); |
| 103 | + var audienceEditor = newAudienceEditor(); |
| 104 | + var styleEditor = newStyleEditor(); |
| 105 | + |
| 106 | + AgentsUtils.NovelCreator novelCreator = |
| 107 | + AgenticWorkflow.of(AgentsUtils.NovelCreator.class) |
| 108 | + .flow(workflow("seqFlow").sequence(creativeWriter, audienceEditor, styleEditor)) |
| 109 | + .build(); |
| 110 | + |
| 111 | + String story = novelCreator.createNovel("dragons and wizards", "young adults", "fantasy"); |
| 112 | + assertNotNull(story); |
| 113 | + } |
| 114 | + |
| 115 | + @Test |
| 116 | + public void agentAndSequenceHelperTest() { |
| 117 | + var creativeWriter = newCreativeWriter(); |
| 118 | + var audienceEditor = newAudienceEditor(); |
| 119 | + var styleEditor = newStyleEditor(); |
| 120 | + |
| 121 | + AgentsUtils.NovelCreator novelCreator = |
| 122 | + AgenticWorkflow.of(AgentsUtils.NovelCreator.class) |
| 123 | + .flow(workflow("seqFlow").agent(creativeWriter).sequence(audienceEditor, styleEditor)) |
| 124 | + .build(); |
| 125 | + |
| 126 | + String story = novelCreator.createNovel("dragons and wizards", "young adults", "fantasy"); |
| 127 | + assertNotNull(story); |
| 128 | + } |
| 129 | + |
| 130 | + @Test |
| 131 | + public void agentAndSequenceAndAgentHelperTest() { |
| 132 | + var creativeWriter = newCreativeWriter(); |
| 133 | + var audienceEditor = newAudienceEditor(); |
| 134 | + var styleEditor = newStyleEditor(); |
| 135 | + var summaryStory = newSummaryStory(); |
| 136 | + |
| 137 | + AgentsUtils.NovelCreator novelCreator = |
| 138 | + AgenticWorkflow.of(AgentsUtils.NovelCreator.class) |
| 139 | + .flow( |
| 140 | + workflow("seqFlow") |
| 141 | + .agent(creativeWriter) |
| 142 | + .sequence(audienceEditor, styleEditor) |
| 143 | + .agent(summaryStory)) |
| 144 | + .build(); |
| 145 | + |
| 146 | + String story = novelCreator.createNovel("dragons and wizards", "young adults", "fantasy"); |
| 147 | + assertNotNull(story); |
| 148 | + } |
| 149 | + |
| 150 | + @Test |
| 151 | + public void parallelWorkflow() { |
| 152 | + var foodExpert = newFoodExpert(); |
| 153 | + var movieExpert = newMovieExpert(); |
| 154 | + |
| 155 | + Function<AgenticScope, List<EveningPlan>> planEvening = |
| 156 | + input -> { |
| 157 | + List<String> movies = (List<String>) input.readState("movies"); |
| 158 | + List<String> meals = (List<String>) input.readState("meals"); |
| 159 | + |
| 160 | + int max = Math.min(movies.size(), meals.size()); |
| 161 | + return IntStream.range(0, max) |
| 162 | + .mapToObj(i -> new EveningPlan(movies.get(i), meals.get(i))) |
| 163 | + .toList(); |
| 164 | + }; |
| 165 | + |
| 166 | + EveningPlannerAgent eveningPlannerAgent = |
| 167 | + AgenticWorkflow.of(EveningPlannerAgent.class) |
| 168 | + .flow(workflow("parallelFlow").parallel(foodExpert, movieExpert).outputAs(planEvening)) |
| 169 | + .build(); |
| 170 | + List<EveningPlan> result = eveningPlannerAgent.plan("romantic"); |
| 171 | + assertEquals(3, result.size()); |
| 172 | + } |
| 173 | + |
| 174 | + @Test |
| 175 | + public void loopTest() { |
| 176 | + var creativeWriter = newCreativeWriter(); |
| 177 | + var scorer = newStyleScorer(); |
| 178 | + var editor = newStyleEditor(); |
| 179 | + |
| 180 | + Predicate<AgenticScope> until = s -> s.readState("score", 0.0) >= 0.8; |
| 181 | + |
| 182 | + StyledWriter styledWriter = |
| 183 | + AgenticWorkflow.of(StyledWriter.class) |
| 184 | + .flow(workflow("loopFlow").agent(creativeWriter).loop(until, scorer, editor)) |
| 185 | + .build(); |
| 186 | + |
| 187 | + String story = styledWriter.writeStoryWithStyle("dragons and wizards", "fantasy"); |
| 188 | + assertNotNull(story); |
| 189 | + } |
| 190 | + |
| 191 | + @Test |
| 192 | + public void humanInTheLoop() { |
| 193 | + var astrologyAgent = newAstrologyAgent(); |
| 194 | + |
| 195 | + var askSign = |
| 196 | + new Function<AgenticScope, AgenticScope>() { |
| 197 | + @Override |
| 198 | + public AgenticScope apply(AgenticScope holder) { |
| 199 | + System.out.println("What's your star sign?"); |
| 200 | + // var sign = System.console().readLine(); |
| 201 | + holder.writeState("sign", "piscis"); |
| 202 | + return holder; |
| 203 | + } |
| 204 | + }; |
| 205 | + |
| 206 | + String result = |
| 207 | + AgenticWorkflow.of(Agents.HoroscopeAgent.class) |
| 208 | + .flow( |
| 209 | + workflow("humanInTheLoop") |
| 210 | + .inputFrom(askSign) |
| 211 | + // .tasks(tasks -> tasks.callFn(fn(askSign))) // TODO should work too |
| 212 | + .agent(astrologyAgent)) |
| 213 | + .build() |
| 214 | + .invoke("My name is Mario. What is my horoscope?"); |
| 215 | + |
| 216 | + assertNotNull(result); |
| 217 | + } |
80 | 218 | } |
0 commit comments