Skip to content

Introduce Fluent Agent DSL (depends on LangChain4j SNAPSHOT) #677

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/maven-verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ on:
pull_request:
branches:
- main

jobs:
build:
runs-on: ubuntu-latest
Expand All @@ -25,8 +26,10 @@ jobs:

- name: Verify with Maven
run: |
mvn -B -f pom.xml clean install verify
mvn -B -f pom.xml clean install verify \
-pl ",!fluent/agentic" \
-am

- name: Verify Examples with Maven
run: |
mvn -B -f examples/pom.xml clean install verify
mvn -B -f examples/pom.xml clean install verify
65 changes: 65 additions & 0 deletions fluent/agentic/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.serverlessworkflow</groupId>
<artifactId>serverlessworkflow-fluent</artifactId>
<version>8.0.0-SNAPSHOT</version>
</parent>

<name>Serverless Workflow :: Fluent :: Agentic</name>
<artifactId>serverlessworkflow-fluent-agentic</artifactId>

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<version.dev.langchain4j>1.2.0-beta8-SNAPSHOT</version.dev.langchain4j>
</properties>

<dependencies>
<dependency>
<groupId>io.serverlessworkflow</groupId>
<artifactId>serverlessworkflow-experimental-types</artifactId>
</dependency>
<dependency>
<groupId>io.serverlessworkflow</groupId>
<artifactId>serverlessworkflow-fluent-func</artifactId>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-agentic</artifactId>
<version>${version.dev.langchain4j}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-ollama</artifactId>
<scope>test</scope>
<version>1.2.0-SNAPSHOT</version>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2020-Present The Serverless Workflow Specification Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.serverlessworkflow.fluent.agentic;

import static dev.langchain4j.agentic.internal.AgentExecutor.agentsToExecutors;

import dev.langchain4j.agentic.Cognisphere;
import dev.langchain4j.agentic.internal.AgentExecutor;
import dev.langchain4j.agentic.internal.AgentInstance;
import io.serverlessworkflow.impl.expressions.LoopPredicateIndex;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;

public final class AgentAdapters {
private AgentAdapters() {}

public static List<AgentExecutor> toExecutors(Object... agents) {
return agentsToExecutors(Stream.of(agents).map(AgentInstance.class::cast).toList());
}

public static Function<Cognisphere, Object> toFunction(AgentExecutor exec) {
return exec::invoke;
}

public static LoopPredicateIndex<Object, Object> toWhile(Predicate<Cognisphere> exit) {
return (model, item, idx) -> !exit.test((Cognisphere) model);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright 2020-Present The Serverless Workflow Specification Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.serverlessworkflow.fluent.agentic;

import io.serverlessworkflow.fluent.agentic.spi.AgentDoFluent;
import io.serverlessworkflow.fluent.func.FuncCallTaskBuilder;
import io.serverlessworkflow.fluent.func.FuncEmitTaskBuilder;
import io.serverlessworkflow.fluent.func.FuncForTaskBuilder;
import io.serverlessworkflow.fluent.func.FuncForkTaskBuilder;
import io.serverlessworkflow.fluent.func.FuncSetTaskBuilder;
import io.serverlessworkflow.fluent.func.FuncSwitchTaskBuilder;
import io.serverlessworkflow.fluent.func.spi.ConditionalTaskBuilder;
import io.serverlessworkflow.fluent.spec.BaseDoTaskBuilder;
import java.util.function.Consumer;

public class AgentDoTaskBuilder
extends BaseDoTaskBuilder<AgentDoTaskBuilder, AgentTaskItemListBuilder>
implements ConditionalTaskBuilder<AgentDoTaskBuilder>, AgentDoFluent<AgentDoTaskBuilder> {

public AgentDoTaskBuilder() {
super(new AgentTaskItemListBuilder());
}

@Override
protected AgentDoTaskBuilder self() {
return this;
}

@Override
public AgentDoTaskBuilder agent(String name, Object agent) {
this.listBuilder().agent(name, agent);
return self();
}

@Override
public AgentDoTaskBuilder sequence(String name, Object... agents) {
this.listBuilder().sequence(name, agents);
return self();
}

@Override
public AgentDoTaskBuilder loop(String name, Consumer<LoopAgentsBuilder> builder) {
this.listBuilder().loop(name, builder);
return self();
}

@Override
public AgentDoTaskBuilder parallel(String name, Object... agents) {
this.listBuilder().parallel(name, agents);
return self();
}

@Override
public AgentDoTaskBuilder callFn(String name, Consumer<FuncCallTaskBuilder> cfg) {
this.listBuilder().callFn(name, cfg);
return self();
}

@Override
public AgentDoTaskBuilder emit(String name, Consumer<FuncEmitTaskBuilder> itemsConfigurer) {
this.listBuilder().emit(name, itemsConfigurer);
return self();
}

@Override
public AgentDoTaskBuilder forEach(String name, Consumer<FuncForTaskBuilder> itemsConfigurer) {
this.listBuilder().forEach(name, itemsConfigurer);
return self();
}

@Override
public AgentDoTaskBuilder fork(String name, Consumer<FuncForkTaskBuilder> itemsConfigurer) {
this.listBuilder().fork(name, itemsConfigurer);
return self();
}

@Override
public AgentDoTaskBuilder set(String name, Consumer<FuncSetTaskBuilder> itemsConfigurer) {
this.listBuilder().set(name, itemsConfigurer);
return self();
}

@Override
public AgentDoTaskBuilder set(String name, String expr) {
this.listBuilder().set(name, expr);
return self();
}

@Override
public AgentDoTaskBuilder switchCase(
String name, Consumer<FuncSwitchTaskBuilder> itemsConfigurer) {
this.listBuilder().switchCase(name, itemsConfigurer);
return self();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Copyright 2020-Present The Serverless Workflow Specification Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.serverlessworkflow.fluent.agentic;

import dev.langchain4j.agentic.internal.AgentExecutor;
import io.serverlessworkflow.api.types.Task;
import io.serverlessworkflow.api.types.TaskItem;
import io.serverlessworkflow.fluent.agentic.spi.AgentDoFluent;
import io.serverlessworkflow.fluent.func.FuncCallTaskBuilder;
import io.serverlessworkflow.fluent.func.FuncEmitTaskBuilder;
import io.serverlessworkflow.fluent.func.FuncForTaskBuilder;
import io.serverlessworkflow.fluent.func.FuncForkTaskBuilder;
import io.serverlessworkflow.fluent.func.FuncSetTaskBuilder;
import io.serverlessworkflow.fluent.func.FuncSwitchTaskBuilder;
import io.serverlessworkflow.fluent.func.FuncTaskItemListBuilder;
import io.serverlessworkflow.fluent.spec.BaseTaskItemListBuilder;
import java.util.List;
import java.util.function.Consumer;

public class AgentTaskItemListBuilder extends BaseTaskItemListBuilder<AgentTaskItemListBuilder>
implements AgentDoFluent<AgentTaskItemListBuilder> {

private final FuncTaskItemListBuilder delegate;

public AgentTaskItemListBuilder() {
super();
this.delegate = new FuncTaskItemListBuilder(super.mutableList());
}

@Override
protected AgentTaskItemListBuilder self() {
return this;
}

@Override
protected AgentTaskItemListBuilder newItemListBuilder() {
return new AgentTaskItemListBuilder();
}

@Override
public AgentTaskItemListBuilder agent(String name, Object agent) {
AgentAdapters.toExecutors(agent)
.forEach(
exec -> this.delegate.callFn(name, fn -> fn.function(AgentAdapters.toFunction(exec))));
return self();
}

@Override
public AgentTaskItemListBuilder sequence(String name, Object... agents) {
for (int i = 0; i < agents.length; i++) {
agent(name + "-" + i, agents[i]);
}
return self();
}

@Override
public AgentTaskItemListBuilder loop(String name, Consumer<LoopAgentsBuilder> consumer) {
final LoopAgentsBuilder builder = new LoopAgentsBuilder();
consumer.accept(builder);
this.addTaskItem(new TaskItem(name, new Task().withForTask(builder.build())));
return self();
}

@Override
public AgentTaskItemListBuilder parallel(String name, Object... agents) {
this.delegate.fork(
name,
fork -> {
List<AgentExecutor> execs = AgentAdapters.toExecutors(agents);
for (int i = 0; i < execs.size(); i++) {
AgentExecutor ex = execs.get(i);
fork.branch("branch-" + i + "-" + name, AgentAdapters.toFunction(ex));
}
});
return self();
}

@Override
public AgentTaskItemListBuilder callFn(String name, Consumer<FuncCallTaskBuilder> cfg) {
this.delegate.callFn(name, cfg);
return self();
}

@Override
public AgentTaskItemListBuilder emit(String name, Consumer<FuncEmitTaskBuilder> itemsConfigurer) {
this.delegate.emit(name, itemsConfigurer);
return self();
}

@Override
public AgentTaskItemListBuilder forEach(
String name, Consumer<FuncForTaskBuilder> itemsConfigurer) {
this.delegate.forEach(name, itemsConfigurer);
return self();
}

@Override
public AgentTaskItemListBuilder fork(String name, Consumer<FuncForkTaskBuilder> itemsConfigurer) {
this.delegate.fork(name, itemsConfigurer);
return self();
}

@Override
public AgentTaskItemListBuilder set(String name, Consumer<FuncSetTaskBuilder> itemsConfigurer) {
this.delegate.set(name, itemsConfigurer);
return self();
}

@Override
public AgentTaskItemListBuilder set(String name, String expr) {
this.delegate.set(name, expr);
return self();
}

@Override
public AgentTaskItemListBuilder switchCase(
String name, Consumer<FuncSwitchTaskBuilder> itemsConfigurer) {
this.delegate.switchCase(name, itemsConfigurer);
return self();
}
}
Loading