|
23 | 23 | import static org.mockito.Mockito.mock;
|
24 | 24 | import static org.mockito.Mockito.when;
|
25 | 25 |
|
| 26 | +import dev.langchain4j.agentic.AgentServices; |
| 27 | +import dev.langchain4j.agentic.workflow.HumanInTheLoop; |
26 | 28 | import io.serverlessworkflow.api.types.Workflow;
|
27 | 29 | import io.serverlessworkflow.impl.WorkflowApplication;
|
28 | 30 | import java.util.HashMap;
|
29 | 31 | import java.util.List;
|
30 | 32 | import java.util.Map;
|
31 | 33 | import java.util.Set;
|
32 | 34 | import java.util.concurrent.ExecutionException;
|
| 35 | +import java.util.concurrent.atomic.AtomicReference; |
33 | 36 | import java.util.function.Function;
|
34 | 37 | import java.util.stream.Collectors;
|
35 | 38 | import org.junit.jupiter.api.Test;
|
@@ -220,4 +223,53 @@ public void testSeqAndThenParallel() throws ExecutionException, InterruptedExcep
|
220 | 223 | assertEquals(technologyTraits, result.get("branch-1-cultureAndTechnology"));
|
221 | 224 | }
|
222 | 225 | }
|
| 226 | + |
| 227 | + @Test |
| 228 | + public void humanInTheLoop() throws ExecutionException, InterruptedException { |
| 229 | + final MeetingInvitationDraft meetingInvitationDraft = mock(MeetingInvitationDraft.class); |
| 230 | + when(meetingInvitationDraft.invoke(eq("Meeting with John Doe"), |
| 231 | + eq("2023-10-01"), eq("08:00AM"), |
| 232 | + eq("London"), |
| 233 | + eq("Discuss project updates"))) |
| 234 | + .thenReturn("Drafted meeting invitation for John Doe"); |
| 235 | + when(meetingInvitationDraft.outputName()).thenReturn("draft"); |
| 236 | + |
| 237 | + |
| 238 | + final MeetingInvitationStyle meetingInvitationStyle = mock(MeetingInvitationStyle.class); |
| 239 | + when(meetingInvitationStyle.invoke(eq("Drafted meeting invitation for John Doe"), eq("formal"))) |
| 240 | + .thenReturn("Styled meeting invitation for John Doe"); |
| 241 | + when(meetingInvitationStyle.outputName()).thenReturn("styled"); |
| 242 | + |
| 243 | + AtomicReference<String> request = new AtomicReference<>(); |
| 244 | + |
| 245 | + HumanInTheLoop humanInTheLoop = AgentServices.humanInTheLoopBuilder() |
| 246 | + .description("What level of formality would you like? (please reply with “formal”, “casual”, or “friendly”)") |
| 247 | + .inputName("style") |
| 248 | + .outputName("style") |
| 249 | + .requestWriter(q -> request.set("What level of formality would you like? (please reply with “formal”, “casual”, or “friendly”)")) |
| 250 | + .responseReader(() -> "formal") |
| 251 | + .build(); |
| 252 | + |
| 253 | + Workflow workflow = |
| 254 | + AgentWorkflowBuilder.workflow("meetingInvitationFlow") |
| 255 | + .tasks( |
| 256 | + d -> |
| 257 | + d.sequence("draft", meetingInvitationDraft, humanInTheLoop, meetingInvitationStyle) |
| 258 | + ).build(); |
| 259 | + Map<String, String> initialValues = new HashMap<>(); |
| 260 | + initialValues.put("title", "Meeting with John Doe"); |
| 261 | + initialValues.put("date", "2023-10-01"); |
| 262 | + initialValues.put("time", "08:00AM"); |
| 263 | + initialValues.put("location", "London"); |
| 264 | + initialValues.put("agenda", "Discuss project updates"); |
| 265 | + |
| 266 | + try (WorkflowApplication app = WorkflowApplication.builder().build()) { |
| 267 | + String result = |
| 268 | + app.workflowDefinition(workflow).instance(initialValues).start().get().asText().orElseThrow(); |
| 269 | + |
| 270 | + assertEquals("Styled meeting invitation for John Doe", result); |
| 271 | + } |
| 272 | + |
| 273 | + |
| 274 | + } |
223 | 275 | }
|
0 commit comments