Skip to content

Commit 8bb44ac

Browse files
committed
Introduce LangChain4j Agentic Workflow Implementation
Signed-off-by: Ricardo Zanini <ricardozanini@gmail.com>
1 parent 8d85aea commit 8bb44ac

File tree

32 files changed

+1434
-127
lines changed

32 files changed

+1434
-127
lines changed

.github/workflows/maven-verify.yml

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
# This workflow will build a Java project with Maven
2-
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
3-
41
name: sdk-java Verify
52

63
on:
@@ -14,22 +11,42 @@ on:
1411
jobs:
1512
build:
1613
runs-on: ubuntu-latest
14+
1715
steps:
18-
- uses: actions/checkout@v4
16+
# 1. Checkout this repo
17+
- name: Checkout sdk-java
18+
uses: actions/checkout@v4
19+
20+
# 2. (Temporary) Checkout the langchain4j repo at agentic-module
21+
- name: Checkout langchain4j (agentic-module)
22+
uses: actions/checkout@v4
23+
with:
24+
repository: mariofusco/langchain4j
25+
ref: agentic-module
26+
path: langchain4j
1927

28+
# 3. Set up JDK 17 for both builds
2029
- name: Set up JDK 17
2130
uses: actions/setup-java@v4
2231
with:
2332
distribution: temurin
2433
java-version: 17
2534
cache: 'maven'
2635

36+
# 4. Build the external agentic module
37+
- name: Build langchain4j-agentic
38+
run: |
39+
mvn -B -U -T12C clean package -DskipTests
40+
mvn -B -f langchain4j/langchain4j-agentic/pom.xml clean install -DskipTests
41+
42+
# 5. Verify the main sdk-java project, excluding the two agentic modules
2743
- name: Verify with Maven
2844
run: |
2945
mvn -B -f pom.xml clean install verify \
3046
-pl ",!fluent/agentic" -pl ",!experimental/agentic" \
3147
-am
3248
49+
# 6. Verify examples
3350
- name: Verify Examples with Maven
3451
run: |
3552
mvn -B -f examples/pom.xml clean install verify

experimental/agentic/src/main/java/io/serverlessworkflow/impl/expressions/agentic/AgenticModel.java

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,15 @@
1616
package io.serverlessworkflow.impl.expressions.agentic;
1717

1818
import dev.langchain4j.agentic.cognisphere.Cognisphere;
19-
import dev.langchain4j.agentic.cognisphere.ResultWithCognisphere;
2019
import io.serverlessworkflow.impl.WorkflowModel;
2120
import io.serverlessworkflow.impl.expressions.func.JavaModel;
2221
import java.util.Collection;
23-
import java.util.Collections;
2422
import java.util.Optional;
2523

2624
class AgenticModel extends JavaModel {
2725

28-
private final Cognisphere cognisphere;
29-
30-
AgenticModel(Object object, Cognisphere cognisphere) {
31-
super(object);
32-
this.cognisphere = cognisphere;
26+
AgenticModel(Cognisphere cognisphere) {
27+
super(cognisphere);
3328
}
3429

3530
@Override
@@ -39,17 +34,13 @@ public void setObject(Object obj) {
3934

4035
@Override
4136
public Collection<WorkflowModel> asCollection() {
42-
return object instanceof Collection value
43-
? new AgenticModelCollection(value, cognisphere)
44-
: Collections.emptyList();
37+
throw new UnsupportedOperationException("Not supported yet.");
4538
}
4639

4740
@Override
4841
public <T> Optional<T> as(Class<T> clazz) {
4942
if (Cognisphere.class.isAssignableFrom(clazz)) {
50-
return Optional.of(clazz.cast(cognisphere));
51-
} else if (ResultWithCognisphere.class.isAssignableFrom(clazz)) {
52-
return Optional.of(clazz.cast(new ResultWithCognisphere<>(cognisphere, object)));
43+
return Optional.of(clazz.cast(object));
5344
} else {
5445
return super.as(clazz);
5546
}

experimental/agentic/src/main/java/io/serverlessworkflow/impl/expressions/agentic/AgenticModelCollection.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class AgenticModelCollection extends JavaModelCollection {
3737

3838
@Override
3939
protected WorkflowModel nextItem(Object obj) {
40-
return new AgenticModel(obj, cognisphere);
40+
return new AgenticModel((Cognisphere) obj);
4141
}
4242

4343
@Override

experimental/agentic/src/main/java/io/serverlessworkflow/impl/expressions/agentic/AgenticModelFactory.java

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,86 +16,96 @@
1616
package io.serverlessworkflow.impl.expressions.agentic;
1717

1818
import dev.langchain4j.agentic.cognisphere.Cognisphere;
19-
import dev.langchain4j.agentic.cognisphere.CognisphereRegistry;
2019
import io.cloudevents.CloudEvent;
2120
import io.cloudevents.CloudEventData;
2221
import io.serverlessworkflow.impl.WorkflowModel;
2322
import io.serverlessworkflow.impl.WorkflowModelCollection;
2423
import io.serverlessworkflow.impl.WorkflowModelFactory;
24+
import io.serverlessworkflow.impl.expressions.agentic.langchain4j.CognisphereRegistryAssessor;
25+
import io.serverlessworkflow.impl.expressions.func.JavaModel;
2526
import java.time.OffsetDateTime;
2627
import java.util.Map;
2728

2829
class AgenticModelFactory implements WorkflowModelFactory {
2930

30-
private Cognisphere cognisphere = CognisphereRegistry.createEphemeralCognisphere();
31-
32-
private final AgenticModel TrueModel = new AgenticModel(Boolean.TRUE, cognisphere);
33-
private final AgenticModel FalseModel = new AgenticModel(Boolean.FALSE, cognisphere);
34-
private final AgenticModel NullModel = new AgenticModel(null, cognisphere);
35-
36-
public void setCognishere(Cognisphere cognisphere) {
37-
this.cognisphere = cognisphere;
38-
}
39-
31+
/**
32+
* Applies any change to the model after running as task. We will always set it to
33+
* a @DefaultCognisphere object since @AgentExecutor is always adding the output to the
34+
* cognisphere. We just have to make sure that cognisphere is always passed to the next input
35+
* task.
36+
*
37+
* @param prev the global Cognisphere object getting updated by the workflow context
38+
* @param obj the same Cognisphere object updated by the AgentExecutor
39+
* @return the workflow context model holding the cognisphere object.
40+
*/
4041
@Override
4142
public WorkflowModel fromAny(WorkflowModel prev, Object obj) {
42-
((AgenticModel) prev).setObject(obj);
43+
// We ignore `obj` since it's already included in `prev` within the Cognisphere instance
4344
return prev;
4445
}
4546

4647
@Override
4748
public WorkflowModel combine(Map<String, WorkflowModel> workflowVariables) {
48-
return new AgenticModel(workflowVariables, cognisphere);
49+
// TODO: create a new cognisphere object in the CognisphereRegistryAssessor per branch
50+
// TODO: Since we share the same cognisphere object, both branches are updating the same
51+
// instance, so for now we return the first key.
52+
return workflowVariables.values().iterator().next();
4953
}
5054

5155
@Override
5256
public WorkflowModelCollection createCollection() {
53-
return new AgenticModelCollection(cognisphere);
57+
throw new UnsupportedOperationException();
5458
}
5559

60+
// TODO: all these methods can use Cognisphere as long as we have access to the `outputName`
61+
5662
@Override
5763
public WorkflowModel from(boolean value) {
58-
return value ? TrueModel : FalseModel;
64+
return new JavaModel(value);
5965
}
6066

6167
@Override
6268
public WorkflowModel from(Number value) {
63-
return new AgenticModel(value, cognisphere);
69+
return new JavaModel(value);
6470
}
6571

6672
@Override
6773
public WorkflowModel from(String value) {
68-
return new AgenticModel(value, cognisphere);
74+
return new JavaModel(value);
6975
}
7076

7177
@Override
7278
public WorkflowModel from(CloudEvent ce) {
73-
return new AgenticModel(ce, cognisphere);
79+
return new JavaModel(ce);
7480
}
7581

7682
@Override
7783
public WorkflowModel from(CloudEventData ce) {
78-
return new AgenticModel(ce, cognisphere);
84+
return new JavaModel(ce);
7985
}
8086

8187
@Override
8288
public WorkflowModel from(OffsetDateTime value) {
83-
return new AgenticModel(value, cognisphere);
89+
return new JavaModel(value);
8490
}
8591

8692
@Override
8793
public WorkflowModel from(Map<String, Object> map) {
94+
final Cognisphere cognisphere = new CognisphereRegistryAssessor().getCognisphere();
8895
cognisphere.writeStates(map);
89-
return new AgenticModel(map, cognisphere);
96+
return new AgenticModel(cognisphere);
9097
}
9198

9299
@Override
93100
public WorkflowModel fromNull() {
94-
return NullModel;
101+
return new JavaModel(null);
95102
}
96103

97104
@Override
98105
public WorkflowModel fromOther(Object value) {
99-
return new AgenticModel(value, cognisphere);
106+
if (value instanceof Cognisphere) {
107+
return new AgenticModel((Cognisphere) value);
108+
}
109+
return new JavaModel(value);
100110
}
101111
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright 2020-Present The Serverless Workflow Specification Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.serverlessworkflow.impl.expressions.agentic.langchain4j;
17+
18+
import dev.langchain4j.agentic.cognisphere.CognisphereRegistry;
19+
import dev.langchain4j.agentic.cognisphere.DefaultCognisphere;
20+
import dev.langchain4j.agentic.internal.CognisphereOwner;
21+
import java.util.Objects;
22+
import java.util.UUID;
23+
import java.util.concurrent.atomic.AtomicReference;
24+
25+
public class CognisphereRegistryAssessor implements CognisphereOwner {
26+
27+
private final AtomicReference<CognisphereRegistry> cognisphereRegistry = new AtomicReference<>();
28+
private final String agentId;
29+
private DefaultCognisphere cognisphere;
30+
private Object memoryId;
31+
32+
public CognisphereRegistryAssessor(String agentId) {
33+
Objects.requireNonNull(agentId, "Agent id cannot be null");
34+
this.agentId = agentId;
35+
}
36+
37+
// TODO: have access to the workflow definition and assign its name instead
38+
public CognisphereRegistryAssessor() {
39+
this.agentId = UUID.randomUUID().toString();
40+
}
41+
42+
public void setMemoryId(Object memoryId) {
43+
this.memoryId = memoryId;
44+
}
45+
46+
public DefaultCognisphere getCognisphere() {
47+
if (cognisphere != null) {
48+
return cognisphere;
49+
}
50+
51+
if (memoryId != null) {
52+
this.cognisphere = registry().getOrCreate(memoryId);
53+
} else {
54+
this.cognisphere = registry().createEphemeralCognisphere();
55+
}
56+
return this.cognisphere;
57+
}
58+
59+
@Override
60+
public CognisphereOwner withCognisphere(DefaultCognisphere cognisphere) {
61+
this.cognisphere = cognisphere;
62+
return this;
63+
}
64+
65+
@Override
66+
public CognisphereRegistry registry() {
67+
cognisphereRegistry.compareAndSet(null, new CognisphereRegistry(agentId));
68+
return cognisphereRegistry.get();
69+
}
70+
}

experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaModel.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public class JavaModel implements WorkflowModel {
2828

2929
protected Object object;
3030

31-
protected JavaModel(Object object) {
31+
public JavaModel(Object object) {
3232
this.object = asJavaObject(object);
3333
}
3434

fluent/agentic-langchain4j/pom.xml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>io.serverlessworkflow</groupId>
8+
<artifactId>serverlessworkflow-fluent</artifactId>
9+
<version>8.0.0-SNAPSHOT</version>
10+
</parent>
11+
12+
<artifactId>serverlessworkflow-fluent-agentic-langchain4j</artifactId>
13+
<name>Serverless Workflow :: Fluent :: Agentic LangChain4j</name>
14+
<description>Agentic Workflow DSL Implementation for langchain4j-agentic</description>
15+
16+
<dependencies>
17+
<dependency>
18+
<groupId>io.serverlessworkflow</groupId>
19+
<artifactId>serverlessworkflow-experimental-agentic</artifactId>
20+
</dependency>
21+
<dependency>
22+
<groupId>io.serverlessworkflow</groupId>
23+
<artifactId>serverlessworkflow-fluent-agentic</artifactId>
24+
</dependency>
25+
<dependency>
26+
<groupId>dev.langchain4j</groupId>
27+
<artifactId>langchain4j-agentic</artifactId>
28+
</dependency>
29+
30+
<dependency>
31+
<groupId>org.slf4j</groupId>
32+
<artifactId>slf4j-simple</artifactId>
33+
<scope>test</scope>
34+
</dependency>
35+
<dependency>
36+
<groupId>org.junit.jupiter</groupId>
37+
<artifactId>junit-jupiter-api</artifactId>
38+
<scope>test</scope>
39+
</dependency>
40+
<dependency>
41+
<groupId>org.mockito</groupId>
42+
<artifactId>mockito-core</artifactId>
43+
<scope>test</scope>
44+
</dependency>
45+
<dependency>
46+
<groupId>org.assertj</groupId>
47+
<artifactId>assertj-core</artifactId>
48+
<scope>test</scope>
49+
</dependency>
50+
<dependency>
51+
<groupId>dev.langchain4j</groupId>
52+
<artifactId>langchain4j-ollama</artifactId>
53+
<scope>test</scope>
54+
<version>${version.dev.langchain4j}</version>
55+
</dependency>
56+
<dependency>
57+
<groupId>io.serverlessworkflow</groupId>
58+
<artifactId>serverlessworkflow-fluent-agentic</artifactId>
59+
<type>test-jar</type>
60+
<scope>test</scope>
61+
</dependency>
62+
</dependencies>
63+
64+
65+
66+
67+
68+
</project>

0 commit comments

Comments
 (0)