Skip to content

Commit 231d058

Browse files
authored
add better docs about Langchain4g/ServerlessWorkflow (#818)
* add better docs about Langchain4g/ServerlessWorkflow Signed-off-by: Dmitrii Tikhomirov <chani.liet@gmail.com> * no try-catch-blocks Signed-off-by: Dmitrii Tikhomirov <chani.liet@gmail.com> * docs and tests are updated Signed-off-by: Dmitrii Tikhomirov <chani.liet@gmail.com> * we can do better Signed-off-by: Dmitrii Tikhomirov <chani.liet@gmail.com> * fix style Signed-off-by: Dmitrii Tikhomirov <chani.liet@gmail.com> * one more fix Signed-off-by: Dmitrii Tikhomirov <chani.liet@gmail.com> --------- Signed-off-by: Dmitrii Tikhomirov <chani.liet@gmail.com>
1 parent dbcfd33 commit 231d058

File tree

7 files changed

+443
-738
lines changed

7 files changed

+443
-738
lines changed

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

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

18-
import dev.langchain4j.agent.tool.Tool;
1918
import dev.langchain4j.agentic.Agent;
20-
import dev.langchain4j.agentic.scope.AgenticScopeAccess;
21-
import dev.langchain4j.service.MemoryId;
2219
import dev.langchain4j.service.UserMessage;
2320
import dev.langchain4j.service.V;
24-
import java.util.List;
2521

2622
public class Agents {
2723

28-
public interface ExpertRouterAgent {
29-
30-
@Agent
31-
String ask(@V("request") String request);
32-
}
33-
34-
public interface ExpertRouterAgentWithMemory extends AgenticScopeAccess {
35-
36-
@Agent
37-
String ask(@MemoryId String memoryId, @V("request") String request);
38-
}
39-
40-
public interface CategoryRouter {
41-
42-
@UserMessage(
43-
"""
44-
Analyze the following user request and categorize it as 'legal', 'medical' or 'technical'.
45-
In case the request doesn't belong to any of those categories categorize it as 'unknown'.
46-
Reply with only one of those words and nothing else.
47-
The user request is: '{{request}}'.
48-
""")
49-
@Agent("Categorize a user request")
50-
RequestCategory classify(@V("request") String request);
51-
}
52-
53-
public enum RequestCategory {
54-
LEGAL,
55-
MEDICAL,
56-
TECHNICAL,
57-
UNKNOWN
58-
}
59-
60-
public interface RouterAgent {
61-
62-
@UserMessage(
63-
"""
64-
Analyze the following user request and categorize it as 'legal', 'medical' or 'technical',
65-
then forward the request as it is to the corresponding expert provided as a tool.
66-
Finally return the answer that you received from the expert without any modification.
67-
68-
The user request is: '{{it}}'.
69-
""")
70-
@Agent
71-
String askToExpert(String request);
72-
}
73-
74-
public interface MedicalExpert {
75-
76-
@UserMessage(
77-
"""
78-
You are a medical expert.
79-
Analyze the following user request under a medical point of view and provide the best possible answer.
80-
The user request is {{request}}.
81-
""")
82-
@Tool("A medical expert")
83-
@Agent("A medical expert")
84-
String medical(@V("request") String request);
85-
}
86-
87-
public interface MedicalExpertWithMemory {
88-
89-
@UserMessage(
90-
"""
91-
You are a medical expert.
92-
Analyze the following user request under a medical point of view and provide the best possible answer.
93-
The user request is {{request}}.
94-
""")
95-
@Tool("A medical expert")
96-
@Agent("A medical expert")
97-
String medical(@MemoryId String memoryId, @V("request") String request);
98-
}
99-
100-
public interface LegalExpert {
101-
102-
@UserMessage(
103-
"""
104-
You are a legal expert.
105-
Analyze the following user request under a legal point of view and provide the best possible answer.
106-
The user request is {{request}}.
107-
""")
108-
@Tool("A legal expert")
109-
@Agent("A legal expert")
110-
String legal(@V("request") String request);
111-
}
112-
113-
public interface LegalExpertWithMemory {
114-
115-
@UserMessage(
116-
"""
117-
You are a legal expert.
118-
Analyze the following user request under a legal point of view and provide the best possible answer.
119-
The user request is {{request}}.
120-
""")
121-
@Tool("A legal expert")
122-
@Agent("A legal expert")
123-
String legal(@MemoryId String memoryId, @V("request") String request);
124-
}
125-
126-
public interface TechnicalExpert {
127-
128-
@UserMessage(
129-
"""
130-
You are a technical expert.
131-
Analyze the following user request under a technical point of view and provide the best possible answer.
132-
The user request is {{request}}.
133-
""")
134-
@Tool("A technical expert")
135-
@Agent("A technical expert")
136-
String technical(@V("request") String request);
137-
}
138-
139-
public interface TechnicalExpertWithMemory {
140-
141-
@UserMessage(
142-
"""
143-
You are a technical expert.
144-
Analyze the following user request under a technical point of view and provide the best possible answer.
145-
The user request is {{request}}.
146-
""")
147-
@Tool("A technical expert")
148-
@Agent("A technical expert")
149-
String technical(@MemoryId String memoryId, @V("request") String request);
150-
}
151-
15224
public interface CreativeWriter {
15325

15426
@UserMessage(
@@ -187,71 +59,4 @@ public interface StyleEditor {
18759
@Agent("Edit a story to better fit a given style")
18860
String editStory(@V("story") String story, @V("style") String style);
18961
}
190-
191-
public interface StyleScorer {
192-
193-
@UserMessage(
194-
"""
195-
You are a critical reviewer.
196-
Give a review score between 0.0 and 1.0 for the following story based on how well it aligns with the style '{{style}}'.
197-
Return only the score and nothing else.
198-
199-
The story is: "{{story}}"
200-
""")
201-
@Agent("Score a story based on how well it aligns with a given style")
202-
double scoreStyle(@V("story") String story, @V("style") String style);
203-
}
204-
205-
public interface StyleReviewLoop {
206-
207-
@Agent("Review the given story to ensure it aligns with the specified style")
208-
String scoreAndReview(@V("story") String story, @V("style") String style);
209-
}
210-
211-
public interface StyledWriter extends AgenticScopeAccess {
212-
213-
@Agent
214-
String writeStoryWithStyle(@V("topic") String topic, @V("style") String style);
215-
}
216-
217-
public interface FoodExpert {
218-
219-
@UserMessage(
220-
"""
221-
You are a great evening planner.
222-
Propose a list of 3 meals matching the given mood.
223-
The mood is {{mood}}.
224-
For each meal, just give the name of the meal.
225-
Provide a list with the 3 items and nothing else.
226-
""")
227-
@Agent
228-
List<String> findMeal(@V("mood") String mood);
229-
}
230-
231-
public interface MovieExpert {
232-
233-
@UserMessage(
234-
"""
235-
You are a great evening planner.
236-
Propose a list of 3 movies matching the given mood.
237-
The mood is {mood}.
238-
Provide a list with the 3 items and nothing else.
239-
""")
240-
@Agent
241-
List<String> findMovie(@V("mood") String mood);
242-
}
243-
244-
public record EveningPlan(String movie, String meal) {}
245-
246-
public interface EveningPlannerAgent {
247-
248-
@Agent
249-
List<EveningPlan> plan(@V("mood") String mood);
250-
}
251-
252-
public interface HoroscopeAgent {
253-
254-
@Agent
255-
String invoke(@V("name") String name);
256-
}
25762
}

experimental/fluent/agentic-langchain4j/src/test/java/io/serverlessworkflow/fluent/agentic/langchain4j/WorkflowAgentsIT.java

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

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.*;
2818
import static io.serverlessworkflow.fluent.agentic.langchain4j.Agents.AudienceEditor;
2919
import static io.serverlessworkflow.fluent.agentic.langchain4j.Agents.CreativeWriter;
3020
import static io.serverlessworkflow.fluent.agentic.langchain4j.Agents.StyleEditor;
3121
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;
3422
import static org.mockito.ArgumentMatchers.any;
3523
import static org.mockito.ArgumentMatchers.eq;
3624
import static org.mockito.Mockito.spy;
3725
import static org.mockito.Mockito.verify;
3826

3927
import dev.langchain4j.agentic.AgenticServices;
4028
import dev.langchain4j.agentic.UntypedAgent;
41-
import dev.langchain4j.agentic.scope.AgenticScope;
4229
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;
4630
import java.util.Map;
47-
import java.util.function.Function;
48-
import java.util.function.Predicate;
49-
import java.util.stream.IntStream;
5031
import org.junit.jupiter.api.Test;
5132

5233
public class WorkflowAgentsIT {
@@ -96,123 +77,4 @@ void sequential_agents_tests() {
9677
verify(audienceEditor).editStory(any(), eq("young adults"));
9778
verify(styleEditor).editStory(any(), eq("fantasy"));
9879
}
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-
}
21880
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public static AgentTaskConfigurer loop(Predicate<AgenticScope> exitCondition, Ob
122122
}
123123

124124
public static AgentTaskConfigurer loop(
125-
Predicate<AgenticScope> exitCondition, int maxIterations, Object... agents) {
125+
int maxIterations, Predicate<AgenticScope> exitCondition, Object... agents) {
126126
return list ->
127127
list.loop(
128128
l -> l.subAgents(agents).exitCondition(exitCondition).maxIterations(maxIterations));

0 commit comments

Comments
 (0)