From c1044fd7013ca68befc28e7a6af5d58e08ce12f9 Mon Sep 17 00:00:00 2001 From: bsorrentino Date: Wed, 22 Jan 2025 20:24:58 +0100 Subject: [PATCH 1/7] build: bump version to SNAPSHOT --- agent-executor/pom.xml | 2 +- core/pom.xml | 2 +- how-tos/pom.xml | 5 +++-- langchain4j/pom.xml | 2 +- pom.xml | 2 +- samples/adaptive-rag/pom.xml | 2 +- samples/image-to-diagram/pom.xml | 2 +- samples/springai-agentexecutor/pom.xml | 2 +- studio/pom.xml | 2 +- 9 files changed, 11 insertions(+), 10 deletions(-) diff --git a/agent-executor/pom.xml b/agent-executor/pom.xml index 3931a58..817c1ee 100644 --- a/agent-executor/pom.xml +++ b/agent-executor/pom.xml @@ -4,7 +4,7 @@ org.bsc.langgraph4j langgraph4j-parent - 1.2.4 + 1.2-SNAPSHOT .. diff --git a/core/pom.xml b/core/pom.xml index 224f995..a74ad2b 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -5,7 +5,7 @@ org.bsc.langgraph4j langgraph4j-parent - 1.2.4 + 1.2-SNAPSHOT langgraph4j-core diff --git a/how-tos/pom.xml b/how-tos/pom.xml index d77e6af..3d0971e 100644 --- a/how-tos/pom.xml +++ b/how-tos/pom.xml @@ -4,13 +4,14 @@ org.bsc.langgraph4j langgraph4j-parent - 1.2.4 + 1.2-SNAPSHOT langgraph4j-howtos - pom langgraph4j::how-tos + Langgraph4j HOW-TOS + jar 17 diff --git a/langchain4j/pom.xml b/langchain4j/pom.xml index 498b08a..e7fdde8 100644 --- a/langchain4j/pom.xml +++ b/langchain4j/pom.xml @@ -5,7 +5,7 @@ org.bsc.langgraph4j langgraph4j-parent - 1.2.4 + 1.2-SNAPSHOT langgraph4j-langchain4j diff --git a/pom.xml b/pom.xml index 6d45442..530fa47 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.bsc.langgraph4j langgraph4j-parent - 1.2.4 + 1.2-SNAPSHOT pom langgraph4j::parent diff --git a/samples/adaptive-rag/pom.xml b/samples/adaptive-rag/pom.xml index bddcfa8..cb85e2e 100644 --- a/samples/adaptive-rag/pom.xml +++ b/samples/adaptive-rag/pom.xml @@ -4,7 +4,7 @@ org.bsc.langgraph4j langgraph4j-parent - 1.2.4 + 1.2-SNAPSHOT ../.. diff --git a/samples/image-to-diagram/pom.xml b/samples/image-to-diagram/pom.xml index f11aabd..9decc98 100644 --- a/samples/image-to-diagram/pom.xml +++ b/samples/image-to-diagram/pom.xml @@ -4,7 +4,7 @@ org.bsc.langgraph4j langgraph4j-parent - 1.2.4 + 1.2-SNAPSHOT ../.. diff --git a/samples/springai-agentexecutor/pom.xml b/samples/springai-agentexecutor/pom.xml index a38fe3d..45573b8 100644 --- a/samples/springai-agentexecutor/pom.xml +++ b/samples/springai-agentexecutor/pom.xml @@ -11,7 +11,7 @@ org.bsc.langgraph4j langgraph4j-springai-agentexecutor - 1.2.4 + 1.2-SNAPSHOT jar langgraph4j::sample::springai::agentexecutor diff --git a/studio/pom.xml b/studio/pom.xml index 7b9441a..16513d0 100644 --- a/studio/pom.xml +++ b/studio/pom.xml @@ -6,7 +6,7 @@ org.bsc.langgraph4j langgraph4j-parent - 1.2.4 + 1.2-SNAPSHOT langgraph4j-studio From c356d9275f85339f6ed600e697f61a091d5cbae7 Mon Sep 17 00:00:00 2001 From: bsorrentino Date: Wed, 22 Jan 2025 20:26:23 +0100 Subject: [PATCH 2/7] refactor(pom.xml): consolidate profiles - Removed unused jdk-8 and jdk-17 profiles to streamline configuration and reduce complexity. --- pom.xml | 40 +++++++++++----------------------------- 1 file changed, 11 insertions(+), 29 deletions(-) diff --git a/pom.xml b/pom.xml index 530fa47..950c11d 100644 --- a/pom.xml +++ b/pom.xml @@ -184,36 +184,18 @@ - - - jdk-8 - - 1.8 - - - - + + core + langchain4j + agent-executor + studio + how-tos + samples/image-to-diagram + samples/adaptive-rag + samples/springai-agentexecutor + - - - - jdk-17 - - [17,) - - - core - langchain4j - agent-executor - studio - how-tos - samples/image-to-diagram - samples/adaptive-rag - samples/springai-agentexecutor - - - - + release From d95f28191e8f8f9112500002fb82ec132f7eee7c Mon Sep 17 00:00:00 2001 From: bsorrentino Date: Thu, 23 Jan 2025 17:53:05 +0100 Subject: [PATCH 3/7] feat(AppenderChannel): introduce functional interface and refactoring for list updates - Introduced `RemoveByHash` class. - Refactored `AppenderChannel.java` to support list updates with a functional interface `RemoveIdentifier`. - Consolidated the logic for evaluating list updates within the `evaluateRemoval` method. - Updated test cases to cover the new functionality. resolve #75 --- .../langgraph4j/state/AppenderChannel.java | 77 ++++++++++++++++--- .../bsc/langgraph4j/state/RemoveByHash.java | 14 ++++ .../org/bsc/langgraph4j/StateGraphTest.java | 76 ++++++++++++++++-- 3 files changed, 153 insertions(+), 14 deletions(-) create mode 100644 core/src/main/java/org/bsc/langgraph4j/state/RemoveByHash.java diff --git a/core/src/main/java/org/bsc/langgraph4j/state/AppenderChannel.java b/core/src/main/java/org/bsc/langgraph4j/state/AppenderChannel.java index c45ccd2..e86a117 100644 --- a/core/src/main/java/org/bsc/langgraph4j/state/AppenderChannel.java +++ b/core/src/main/java/org/bsc/langgraph4j/state/AppenderChannel.java @@ -2,12 +2,11 @@ import lombok.extern.slf4j.Slf4j; -import java.lang.reflect.Array; import java.util.*; import java.util.function.Supplier; +import static java.util.Collections.unmodifiableList; import static java.util.Optional.ofNullable; -import static org.bsc.langgraph4j.utils.CollectionsUtils.listOf; /** @@ -20,6 +19,16 @@ @Slf4j public class AppenderChannel implements Channel> { + /** + * A functional interface that is used to remove elements from a list. + * + * @param the type of elements in the list + */ + @FunctionalInterface + public interface RemoveIdentifier { + int compareTo(T element, int atIndex ); + } + private final Reducer> reducer; private final Supplier> defaultProvider; @@ -58,16 +67,14 @@ public static AppenderChannel of( Supplier> defaultProvider ) { /** * Constructs a new instance of {@code AppenderChannel} with the specified default provider. * - * @param the type of elements in the lists to be processed * @param defaultProvider a supplier for the default list that will be used when no other list is available */ - private AppenderChannel( Supplier> defaultProvider) { - this.reducer = new Reducer>() { + private AppenderChannel( Supplier> defaultProvider ) { + this.reducer = new Reducer<>() { /** * Combines two lists into one. If the first list is null, the second list is returned. * Otherwise, the second list is added to the end of the first list and the resulting list is returned. * - * @param the type of elements in the lists * @param left the first list; may be null * @param right the second list * @return a new list containing all elements from both input lists @@ -84,6 +91,47 @@ public List apply(List left, List right) { this.defaultProvider = defaultProvider; } + private List remove(List list, RemoveIdentifier removeIdentifier ) { + var result = new ArrayList<>(list); + removeFromList(result, removeIdentifier); + return unmodifiableList(result); + } + + private void removeFromList(List result, RemoveIdentifier removeIdentifier ) { + for( int i = 0; i < result.size(); i++ ) { + if( removeIdentifier.compareTo( result.get(i), i ) == 0 ) { + result.remove(i); + break; + } + } + } + + record RemoveData( List oldValues, List newValues) { + + // copy constructor. make sure to copy the list to make them modifiable + public RemoveData { + oldValues = new ArrayList<>(oldValues); + newValues = new ArrayList<>(newValues); + } + }; + + @SuppressWarnings("unchecked") + private RemoveData evaluateRemoval(List oldValues, List newValues ) { + + final var result = new RemoveData<>( oldValues, newValues ); + + newValues.stream() + .filter( value -> value instanceof RemoveIdentifier ) + .forEach( value -> { + result.newValues().remove( value ); + var removeIdentifier = (RemoveIdentifier) value; + removeFromList( result.oldValues(), removeIdentifier ); + + }); + return result; + + } + /** * Updates the value for a given key in the channel. * @@ -95,28 +143,39 @@ public List apply(List left, List right) { * * @throws UnsupportedOperationException If the channel does not support updates, typically due to an immutable list being used. */ + @SuppressWarnings("unchecked") public Object update( String key, Object oldValue, Object newValue) { if( newValue == null ) { return oldValue; } + + boolean oldValueIsList = oldValue instanceof List; + try { + if( oldValueIsList && newValue instanceof RemoveIdentifier ) { + return remove( (List)oldValue, (RemoveIdentifier)newValue); + } List list = null; if (newValue instanceof List) { - list = (List) newValue; + list = (List) newValue; } else if (newValue.getClass().isArray()) { - list = Arrays.asList((Object[]) newValue); + list = Arrays.asList((T[])newValue); } if (list != null) { if (list.isEmpty()) { return oldValue; } + if( oldValueIsList ) { + var result = evaluateRemoval( (List)oldValue, list ); + return Channel.super.update(key, result.oldValues(), result.newValues()); + } return Channel.super.update(key, oldValue, list); } // this is to allow single value other than List or Array try { T typedValue = (T)newValue; - return Channel.super.update(key, oldValue, listOf(typedValue)); + return Channel.super.update(key, oldValue, List.of(typedValue)); } catch (ClassCastException e) { log.error("Unsupported content type: {}", newValue.getClass()); throw e; diff --git a/core/src/main/java/org/bsc/langgraph4j/state/RemoveByHash.java b/core/src/main/java/org/bsc/langgraph4j/state/RemoveByHash.java new file mode 100644 index 0000000..9283bcc --- /dev/null +++ b/core/src/main/java/org/bsc/langgraph4j/state/RemoveByHash.java @@ -0,0 +1,14 @@ +package org.bsc.langgraph4j.state; + +import java.util.Objects; + +public record RemoveByHash(T value ) implements AppenderChannel.RemoveIdentifier { + @Override + public int compareTo(T element, int atIndex) { + return Objects.hashCode(value) - Objects.hashCode(element); + } + + public static RemoveByHash of ( T value ) { + return new RemoveByHash<>(value); + } +} diff --git a/core/src/test/java/org/bsc/langgraph4j/StateGraphTest.java b/core/src/test/java/org/bsc/langgraph4j/StateGraphTest.java index 4281527..a2df0ce 100644 --- a/core/src/test/java/org/bsc/langgraph4j/StateGraphTest.java +++ b/core/src/test/java/org/bsc/langgraph4j/StateGraphTest.java @@ -2,10 +2,7 @@ import lombok.extern.slf4j.Slf4j; -import org.bsc.langgraph4j.state.AgentState; -import org.bsc.langgraph4j.state.AppendableValue; -import org.bsc.langgraph4j.state.AppenderChannel; -import org.bsc.langgraph4j.state.Channel; +import org.bsc.langgraph4j.state.*; import org.junit.jupiter.api.Test; import java.util.*; @@ -162,6 +159,76 @@ void testWithAppender() throws Exception { } + @Test + void testWithAppenderOneRemove() throws Exception { + + StateGraph workflow = new StateGraph<>(MessagesState.SCHEMA, MessagesState::new) + .addNode("agent_1", node_async(state -> { + System.out.println("agent_1"); + return Map.of("messages", "message1"); + })) + .addNode("agent_2", node_async(state -> { + System.out.println("agent_2"); + return Map.of("messages", new String[]{"message2"}); + })) + .addNode("agent_3", node_async(state -> { + System.out.println("agent_3"); + int steps = state.messages().size() + 1; + return Map.of("messages", RemoveByHash.of("message2"), "steps", steps); + })) + .addEdge("agent_1", "agent_2") + .addEdge("agent_2", "agent_3") + .addEdge(START, "agent_1") + .addEdge("agent_3", END); + + CompiledGraph app = workflow.compile(); + + Optional result = app.invoke(Map.of()); + + assertTrue(result.isPresent()); + System.out.println(result.get().data()); + assertEquals(3, result.get().steps()); + assertEquals(1, result.get().messages().size()); + assertIterableEquals(List.of("message1"), result.get().messages()); + + } + + @Test + void testWithAppenderOneAppendOneRemove() throws Exception { + + StateGraph workflow = new StateGraph<>(MessagesState.SCHEMA, MessagesState::new) + .addNode("agent_1", node_async(state -> + Map.of("messages", "message1") + )) + .addNode("agent_2", node_async(state -> + Map.of("messages", new String[]{"message2"}) + )) + .addNode("agent_3", node_async(state -> + Map.of("messages", List.of( "message3", RemoveByHash.of("message2"))) + )) + .addNode("agent_4", node_async(state -> { + int steps = state.messages().size() + 1; + return Map.of("messages", List.of("message4"), "steps", steps); + + })) + .addEdge("agent_1", "agent_2") + .addEdge("agent_2", "agent_3") + .addEdge("agent_3", "agent_4") + .addEdge(START, "agent_1") + .addEdge("agent_4", END); + + CompiledGraph app = workflow.compile(); + + Optional result = app.invoke(Map.of()); + + assertTrue(result.isPresent()); + System.out.println(result.get().data()); + assertEquals(3, result.get().steps()); + assertEquals(3, result.get().messages().size()); + assertIterableEquals(List.of("message1", "message3", "message4"), result.get().messages()); + + } + @Test public void testWithSubgraph() throws Exception { @@ -171,7 +238,6 @@ public void testWithSubgraph() throws Exception { var childStep3 = node_async((MessagesState state) -> Map.of("messages", "child:step3")); - var workflowChild = new StateGraph<>(MessagesState.SCHEMA, MessagesState::new) .addNode("child:step_1", childStep1) .addNode("child:step_2", childStep2) From 807fcab25ea06640e16b61b2fb78e4c888d1ec4f Mon Sep 17 00:00:00 2001 From: bsorrentino Date: Thu, 23 Jan 2025 20:04:57 +0100 Subject: [PATCH 4/7] docs: update documentation work on #75 --- README.md | 1 + core/src/site/markdown/concepts/low_level.md | 121 +++++++++++++------ core/src/site/markdown/index.md | 4 +- src/site/markdown/index.md | 16 +-- 4 files changed, 95 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 60effef..186b52d 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ LangGraph for Java. A library for building stateful, multi-agents applications w - [x] Reducer (_how apply updates to the state attributes_) - [x] Default provider - [x] AppenderChannel (_values accumulator_) + - [x] delete messages - [x] Compiling graph - [x] Async support (_throught [CompletableFuture]_) - [x] Streaming support (_throught [java-async-generator]_) diff --git a/core/src/site/markdown/concepts/low_level.md b/core/src/site/markdown/concepts/low_level.md index a22324b..8be24c8 100644 --- a/core/src/site/markdown/concepts/low_level.md +++ b/core/src/site/markdown/concepts/low_level.md @@ -4,11 +4,11 @@ At its core, LangGraph4j models agent workflows as graphs. You define the behavior of your agents using three key components: -1. [`State`](#State): A shared data structure that represents the current snapshot of your application. It is represented by an [`AgentState`] object. +1. [State](#State): A shared data structure that represents the current snapshot of your application. It is represented by an [AgentState] object. -2. [`Nodes`](#Nodes): A **Functional Interface** ([`AsyncNodeAction`]) that encode the logic of your agents. They receive the current `State` as input, perform some computation or side-effect, and return an updated `State`. +2. [Nodes](#Nodes): A **Functional Interface** ([AsyncNodeAction]) that encode the logic of your agents. They receive the current `State` as input, perform some computation or side-effect, and return an updated `State`. -3. [`Edges`](#Edges): A **Functional Interface** ([`AsyncEdgeAction`]) that determine which `Node` to execute next based on the current `State`. They can be conditional branches or fixed transitions. +3. [Edges](#Edges): A **Functional Interface** ([AsyncEdgeAction]) that determine which `Node` to execute next based on the current `State`. They can be conditional branches or fixed transitions. By composing `Nodes` and `Edges`, you can create complex, looping workflows that evolve the `State` over time. The real power, though, comes from how LangGraph4j manages that `State`. To emphasize: `Nodes` and `Edges` are like functions - they can contain an LLM or just Java code. @@ -23,7 +23,7 @@ A super-step can be considered a single iteration over the graph nodes. Nodes th --> ### StateGraph -The [`StateGraph`] class is the main graph class to uses. This is parameterized by a user defined `State` object. +The [StateGraph] class is the main graph class to uses. This is parameterized by a user defined `State` object. -In LangGraph4j, nodes are typically a **Functional Interface** ([`AsyncNodeAction`]) where the argument is the [state](#State), you add these nodes to a graph using the [`addNode`] method: +In LangGraph4j, nodes are typically a **Functional Interface** ([AsyncNodeAction]) where the argument is the [state](#State), you add these nodes to a graph using the [addNode] method: ```java import static org.bsc.langgraph4j.action.AsyncEdgeAction.edge_async; @@ -159,7 +200,7 @@ var builder = new StateGraph( State::new ) ``` -Since [`AsyncNodeAction`] is designed to work with [`CompletableFuture`], you can use `node_async` static method that adapt it to a simpler syncronous scenario. +Since [AsyncNodeAction] is designed to work with [CompletableFuture], you can use `node_async` static method that adapt it to a simpler syncronous scenario. ### `START` Node @@ -211,7 +252,7 @@ graph.addEdge("nodeA", "nodeB"); ### Conditional Edges -If you want to **optionally** route to 1 or more edges (or optionally terminate), you can use the [`addConditionalEdges`] method. This method accepts the name of a node and a **Functional Interface** ([`AsyncEdgeAction`]) that will be used as " routing function" to call after that node is executed: +If you want to **optionally** route to 1 or more edges (or optionally terminate), you can use the [addConditionalEdges] method. This method accepts the name of a node and a **Functional Interface** ([AsyncEdgeAction]) that will be used as " routing function" to call after that node is executed: ```java graph.addConditionalEdges("nodeA", routingFunction, Map.of( "first": "nodeB", "second": "nodeC" ) ); @@ -226,7 +267,7 @@ You must provide an object that maps the `routingFunction`'s output to the name ### Entry Point -The entry point is the first node(s) that are run when the graph starts. You can use the [`addEdge`] method from the virtual `START` node to the first node to execute to specify where to enter the graph. +The entry point is the first node(s) that are run when the graph starts. You can use the [addEdge] method from the virtual `START` node to the first node to execute to specify where to enter the graph. ```java import static org.bsc.langgraph4j.StateGraph.START; @@ -236,7 +277,7 @@ graph.addEdge(START, "nodeA"); ### Conditional Entry Point -A conditional entry point lets you start at different nodes depending on custom logic. You can use [`addConditionalEdges`] from the virtual `START` node to accomplish this. +A conditional entry point lets you start at different nodes depending on custom logic. You can use [addConditionalEdges] from the virtual `START` node to accomplish this. ```java import static org.bsc.langgraph4j.StateGraph.START; @@ -252,7 +293,7 @@ You must provide an object that maps the `routingFunction`'s output to the name By default, `Nodes` and `Edges` are defined ahead of time and operate on the same shared state. However, there can be cases where the exact edges are not known ahead of time and/or you may want different versions of `State` to exist at the same time. A common of example of this is with `map-reduce` design patterns. In this design pattern, a first node may generate an array of objects, and you may want to apply some other node to all those objects. The number of objects may be unknown ahead of time (meaning the number of edges may not be known) and the input `State` to the downstream `Node` should be different (one for each generated object). -To support this design pattern, LangGraph4j supports returning [`Send`](/langgraphjs/reference/classes/langgraph.Send.html) objects from conditional edges. `Send` takes two arguments: first is the name of the node, and second is the state to pass to that node. +To support this design pattern, LangGraph4j supports returning [Send](/langgraphjs/reference/classes/langgraph.Send.html) objects from conditional edges. `Send` takes two arguments: first is the name of the node, and second is the state to pass to that node. ```typescript const continueToJokes = (state: { subjects: string[] }) => { @@ -264,7 +305,7 @@ graph.addConditionalEdges("nodeA", continueToJokes); ## Checkpointer -LangGraph4j has a built-in persistence layer, implemented through [`Checkpointers`]. When you use a checkpointer with a graph, you can interact with the state of that graph. When you use a checkpointer with a graph, you can interact with and manage the graph's state. The checkpointer saves a _checkpoint_ of the graph state at every step, enabling several powerful capabilities: +LangGraph4j has a built-in persistence layer, implemented through [Checkpointers]. When you use a checkpointer with a graph, you can interact with the state of that graph. When you use a checkpointer with a graph, you can interact with and manage the graph's state. The checkpointer saves a _checkpoint_ of the graph state at every step, enabling several powerful capabilities: First, checkpointers facilitate [human-in-the-loop workflows](agentic_concepts.md#human-in-the-loop) workflows by allowing humans to inspect, interrupt, and approve steps. Checkpointers are needed for these workflows as the human has to be able to view the state of a graph at any point in time, and the graph has to be to resume execution after the human has made any updates to the state. @@ -302,17 +343,17 @@ When interacting with the checkpointer state, you must specify a [thread identif ### Get state -You can get the state of a checkpointer by calling [`graph.getState(config)`]. The config should contain `thread_id`, and the state will be fetched for that thread. +You can get the state of a checkpointer by calling [graph.getState(config)]. The config should contain `thread_id`, and the state will be fetched for that thread. ### Get state history -You can also call [`graph.getStateHistory(config)`] to get a list of the history of the graph. The config should contain `thread_id`, and the state history will be fetched for that thread. +You can also call [graph.getStateHistory(config)] to get a list of the history of the graph. The config should contain `thread_id`, and the state history will be fetched for that thread. ### Update state -You can also interact with the state directly and update it using [`graph.updateState(config,values,asNode)`]. This takes three different components: +You can also interact with the state directly and update it using [graph.updateState(config,values,asNode)]. This takes three different components: - `config` - `values` @@ -385,7 +426,7 @@ See [this guide](../how-tos/breakpoints.html) for a full walkthrough of how to a ## Visualization -It's often nice to be able to visualize graphs, especially as they get more complex. LangGraph4j comes with several built-in ways to visualize graphs using diagram-as-code tools such as [PlantUML] and [Mermaid] through the [`graph.getGraph`] method. +It's often nice to be able to visualize graphs, especially as they get more complex. LangGraph4j comes with several built-in ways to visualize graphs using diagram-as-code tools such as [PlantUML] and [Mermaid] through the [graph.getGraph] method. ```java // for PlantUML @@ -402,30 +443,32 @@ System.out.println(result.getContent()); +In addition, you can use the [streamEvents](https://v02.api.js.langchain.com/classes/langchain_core_runnables.Runnable.html#streamEvents) method to stream back events that happen _inside_ nodes. This is useful for [streaming tokens of LLM calls](../how-tos/streaming-tokens-without-langchain.html). --> [PlainTextStateSerializer]: /langgraph4j/apidocs/org/bsc/langgraph4j/serializer/plain_text/PlainTextStateSerializer.html [ObjectStreamStateSerializer]: /langgraph4j/apidocs/org/bsc/langgraph4j/serializer/std/ObjectStreamStateSerializer.html +[RemoveByHash]: /langgraph4j/apidocs/org/bsc/langgraph4j/state/RemoveByHash.html +[RemoveIdentifier]: /langgraph4j/apidocs/org/bsc/langgraph4j/state/AppenderChannel.RemoveIdentifier.html [Serializer]: /langgraph4j/apidocs/org/bsc/langgraph4j/serializer/Serializer.html [Reducer]: /langgraph4j/apidocs/org/bsc/langgraph4j/state/Reducer.html -[`AgentState`]: /langgraph4j/apidocs/org/bsc/langgraph4j/state/AgentState.html -[`StateGraph`]: /langgraph4j/apidocs/org/bsc/langgraph4j/StateGraph.html -[`Channel`]: /langgraph4j/apidocs/org/bsc/langgraph4j/state/Channel.html -[`AsyncNodeAction`]: /langgraph4j/apidocs/org/bsc/langgraph4j/action/AsyncNodeAction.html -[`AsyncEdgeAction`]: /langgraph4j/apidocs/org/bsc/langgraph4j/action/AsyncEdgeAction.html -[`AppenderChannel`]: /langgraph4j/apidocs/org/bsc/langgraph4j/state/AppenderChannel.html -[`addNode`]: /langgraph4j/apidocs/org/bsc/langgraph4j/StateGraph.html#addNode-java.lang.String-org.bsc.langgraph4j.action.AsyncNodeAction- -[`addEdge`]: /langgraph4j/apidocs/org/bsc/langgraph4j/StateGraph.html#addEdge-java.lang.String-java.lang.String- -[`addConditionalEdges`]: /langgraph4j/apidocs/org/bsc/langgraph4j/StateGraph.html#addConditionalEdges-java.lang.String-org.bsc.langgraph4j.action.AsyncEdgeAction-java.util.Map- -[`CompletableFuture`]: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html -[`Checkpointers`]: /langgraph4j/apidocs/org/bsc/langgraph4j/checkpoint/BaseCheckpointSaver.html -[`graph.updateState(config,values,asNode)`]: /langgraph4j/apidocs/org/bsc/langgraph4j/CompiledGraph.html#updateState-org.bsc.langgraph4j.RunnableConfig-java.util.Map-java.lang.String- -[`graph.getStateHistory(config)`]: /langgraph4j/apidocs/org/bsc/langgraph4j/CompiledGraph.html#getStateHistory-org.bsc.langgraph4j.RunnableConfig- -[`graph.getState(config)`]: /langgraph4j/apidocs/org/bsc/langgraph4j/CompiledGraph.html#getState-org.bsc.langgraph4j.RunnableConfig- +[AgentState]: /langgraph4j/apidocs/org/bsc/langgraph4j/state/AgentState.html +[StateGraph]: /langgraph4j/apidocs/org/bsc/langgraph4j/StateGraph.html +[Channel]: /langgraph4j/apidocs/org/bsc/langgraph4j/state/Channel.html +[AsyncNodeAction]: /langgraph4j/apidocs/org/bsc/langgraph4j/action/AsyncNodeAction.html +[AsyncEdgeAction]: /langgraph4j/apidocs/org/bsc/langgraph4j/action/AsyncEdgeAction.html +[AppenderChannel]: /langgraph4j/apidocs/org/bsc/langgraph4j/state/AppenderChannel.html +[addNode]: /langgraph4j/apidocs/org/bsc/langgraph4j/StateGraph.html#addNode-java.lang.String-org.bsc.langgraph4j.action.AsyncNodeAction- +[addEdge]: /langgraph4j/apidocs/org/bsc/langgraph4j/StateGraph.html#addEdge-java.lang.String-java.lang.String- +[addConditionalEdges]: /langgraph4j/apidocs/org/bsc/langgraph4j/StateGraph.html#addConditionalEdges-java.lang.String-org.bsc.langgraph4j.action.AsyncEdgeAction-java.util.Map- +[CompletableFuture]: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html +[Checkpointers]: /langgraph4j/apidocs/org/bsc/langgraph4j/checkpoint/BaseCheckpointSaver.html +[graph.updateState(config,values,asNode)]: /langgraph4j/apidocs/org/bsc/langgraph4j/CompiledGraph.html#updateState-org.bsc.langgraph4j.RunnableConfig-java.util.Map-java.lang.String- +[graph.getStateHistory(config)]: /langgraph4j/apidocs/org/bsc/langgraph4j/CompiledGraph.html#getStateHistory-org.bsc.langgraph4j.RunnableConfig- +[graph.getState(config)]: /langgraph4j/apidocs/org/bsc/langgraph4j/CompiledGraph.html#getState-org.bsc.langgraph4j.RunnableConfig- [PlantUML]: https://plantuml.com [java-async-generator]: https://github.com/bsorrentino/java-async-generator [Mermaid]: https://mermaid.js.org -[`graph.getGraph`]: /langgraph4j/apidocs/org/bsc/langgraph4j/CompiledGraph.html#getGraph-org.bsc.langgraph4j.GraphRepresentation.Type-java.lang.String- \ No newline at end of file +[graph.getGraph]: /langgraph4j/apidocs/org/bsc/langgraph4j/CompiledGraph.html#getGraph-org.bsc.langgraph4j.GraphRepresentation.Type-java.lang.String- \ No newline at end of file diff --git a/core/src/site/markdown/index.md b/core/src/site/markdown/index.md index 91ca061..7a9eb19 100644 --- a/core/src/site/markdown/index.md +++ b/core/src/site/markdown/index.md @@ -21,8 +21,10 @@ LangGraph4j for Agentic Applications - [State](concepts/low_level.md#State) - [Schema](concepts/low_level.md#Schema) - [Reducers](concepts/low_level.md#Reducers) - + - [MessageState](concepts/low_level.md#AppenderChannel) + - [Remove Messages](concepts/low_level.md#remove-messages) - [Serializer](concepts/low_level.md#Serializer) + - [Out of the Box](concepts/low_level.md#seriliazer-out-of-box) - [Nodes](concepts/low_level.md#Nodes) - [`START` node](concepts/low_level.md#start-node) - [`END` node](concepts/low_level.md#end-node) diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md index 2b616f5..5f5c840 100644 --- a/src/site/markdown/index.md +++ b/src/site/markdown/index.md @@ -15,21 +15,23 @@ LangGraph for Java. A library for building stateful, multi-agents applications w - [x] Entry Points - [x] Conditional Entry Points - [x] State - - [x] Schema (_a series of Channels_) - - [x] Reducer (_how apply updates to the state attributes_) - - [x] Default provider - - [x] AppenderChannel (_values accumulator_) -- [x] Compiling graph + - [x] Schema (_a series of Channels_) + - [x] Reducer (_how apply updates to the state attributes_) + - [x] Default provider + - [x] AppenderChannel (_values accumulator_) + - [x] delete messages +- [x] Compiling graph - [x] Async support (_throught [CompletableFuture]_) - [x] Streaming support (_throught [java-async-generator]_) - [x] Checkpoints (_save and replay feature_) - [x] Graph visualization - - [x] [PlantUML] - - [x] [Mermaid] + - [x] [PlantUML] + - [x] [Mermaid] - [x] Playground (_Embeddable Webapp that plays with LangGraph4j_) - [x] Threads (_checkpointing of multiple different runs_) - [x] Update state (_interact with the state directly and update it_) - [x] Breakpoints (_pause and resume feature_) +- [x] [Studio] (_Playground Webapp_) - [X] Streaming response from LLM results - [X] Child Graphs - [ ] Parallel Node Execution From 0d9ddfad51bec464dd37f36e9d3f1ce9ce6837fb Mon Sep 17 00:00:00 2001 From: bsorrentino Date: Thu, 23 Jan 2025 20:07:07 +0100 Subject: [PATCH 5/7] build: bump to version 1.2.5 --- agent-executor/pom.xml | 2 +- core/pom.xml | 2 +- how-tos/pom.xml | 2 +- langchain4j/pom.xml | 2 +- pom.xml | 2 +- samples/adaptive-rag/pom.xml | 2 +- samples/image-to-diagram/pom.xml | 2 +- samples/springai-agentexecutor/pom.xml | 2 +- studio/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/agent-executor/pom.xml b/agent-executor/pom.xml index 817c1ee..4299df7 100644 --- a/agent-executor/pom.xml +++ b/agent-executor/pom.xml @@ -4,7 +4,7 @@ org.bsc.langgraph4j langgraph4j-parent - 1.2-SNAPSHOT + 1.2.5 .. diff --git a/core/pom.xml b/core/pom.xml index a74ad2b..9af7913 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -5,7 +5,7 @@ org.bsc.langgraph4j langgraph4j-parent - 1.2-SNAPSHOT + 1.2.5 langgraph4j-core diff --git a/how-tos/pom.xml b/how-tos/pom.xml index 3d0971e..e362736 100644 --- a/how-tos/pom.xml +++ b/how-tos/pom.xml @@ -4,7 +4,7 @@ org.bsc.langgraph4j langgraph4j-parent - 1.2-SNAPSHOT + 1.2.5 langgraph4j-howtos diff --git a/langchain4j/pom.xml b/langchain4j/pom.xml index e7fdde8..dcec868 100644 --- a/langchain4j/pom.xml +++ b/langchain4j/pom.xml @@ -5,7 +5,7 @@ org.bsc.langgraph4j langgraph4j-parent - 1.2-SNAPSHOT + 1.2.5 langgraph4j-langchain4j diff --git a/pom.xml b/pom.xml index 950c11d..5497a82 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.bsc.langgraph4j langgraph4j-parent - 1.2-SNAPSHOT + 1.2.5 pom langgraph4j::parent diff --git a/samples/adaptive-rag/pom.xml b/samples/adaptive-rag/pom.xml index cb85e2e..e31687d 100644 --- a/samples/adaptive-rag/pom.xml +++ b/samples/adaptive-rag/pom.xml @@ -4,7 +4,7 @@ org.bsc.langgraph4j langgraph4j-parent - 1.2-SNAPSHOT + 1.2.5 ../.. diff --git a/samples/image-to-diagram/pom.xml b/samples/image-to-diagram/pom.xml index 9decc98..a4df75e 100644 --- a/samples/image-to-diagram/pom.xml +++ b/samples/image-to-diagram/pom.xml @@ -4,7 +4,7 @@ org.bsc.langgraph4j langgraph4j-parent - 1.2-SNAPSHOT + 1.2.5 ../.. diff --git a/samples/springai-agentexecutor/pom.xml b/samples/springai-agentexecutor/pom.xml index 45573b8..8216805 100644 --- a/samples/springai-agentexecutor/pom.xml +++ b/samples/springai-agentexecutor/pom.xml @@ -11,7 +11,7 @@ org.bsc.langgraph4j langgraph4j-springai-agentexecutor - 1.2-SNAPSHOT + 1.2.5 jar langgraph4j::sample::springai::agentexecutor diff --git a/studio/pom.xml b/studio/pom.xml index 16513d0..ef7758f 100644 --- a/studio/pom.xml +++ b/studio/pom.xml @@ -6,7 +6,7 @@ org.bsc.langgraph4j langgraph4j-parent - 1.2-SNAPSHOT + 1.2.5 langgraph4j-studio From 666c25331bfeced290ca20529c32d53ed65c8c5e Mon Sep 17 00:00:00 2001 From: bsorrentino Date: Thu, 23 Jan 2025 20:15:38 +0100 Subject: [PATCH 6/7] docs: update javadoc --- .../langgraph4j/state/AppenderChannel.java | 38 +++++++++++++++++++ .../bsc/langgraph4j/state/RemoveByHash.java | 22 ++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/bsc/langgraph4j/state/AppenderChannel.java b/core/src/main/java/org/bsc/langgraph4j/state/AppenderChannel.java index e86a117..07b4fad 100644 --- a/core/src/main/java/org/bsc/langgraph4j/state/AppenderChannel.java +++ b/core/src/main/java/org/bsc/langgraph4j/state/AppenderChannel.java @@ -26,6 +26,14 @@ public class AppenderChannel implements Channel> { */ @FunctionalInterface public interface RemoveIdentifier { + /** + * Compares the specified element with the element at the given index. + * + * @param the type of elements to compare + * @param element the element to be compared + * @param atIndex the index of the element to compare with + * @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object. + */ int compareTo(T element, int atIndex ); } @@ -91,12 +99,30 @@ public List apply(List left, List right) { this.defaultProvider = defaultProvider; } + /** + * This method removes elements from a given list based on the specified {@link RemoveIdentifier}. + * It creates a copy of the original list, performs the removal operation, and returns an immutable view of the result. + * + * @param The type of elements in the list. + * @param list The list from which elements will be removed. + * @param removeIdentifier An instance of {@link RemoveIdentifier} that defines how to identify elements for removal. + * @return An unmodifiable view of the modified list with specified elements removed. + */ private List remove(List list, RemoveIdentifier removeIdentifier ) { var result = new ArrayList<>(list); removeFromList(result, removeIdentifier); return unmodifiableList(result); } + /** + * Removes an element from the list that matches the specified identifier. + * + *

This method iterates over the provided list and removes the first element for which the + * {@link RemoveIdentifier#compareTo} method returns zero.

+ * + * @param result the list to be modified + * @param removeIdentifier the identifier used to find the element to remove + */ private void removeFromList(List result, RemoveIdentifier removeIdentifier ) { for( int i = 0; i < result.size(); i++ ) { if( removeIdentifier.compareTo( result.get(i), i ) == 0 ) { @@ -106,6 +132,11 @@ private void removeFromList(List result, RemoveIdentifier removeIdentifier } } + /** + * Represents a record for data removal operations with generic types. + * + * @param the type of elements in the old values list + */ record RemoveData( List oldValues, List newValues) { // copy constructor. make sure to copy the list to make them modifiable @@ -115,6 +146,13 @@ record RemoveData( List oldValues, List newValues) { } }; + /** + * Evaluates the removal of identifiers from the new values list and updates the RemoveData object accordingly. + * + * @param oldValues a {@code List} of old values + * @param newValues a {@code List} of new values containing {@code RemoveIdentifier}s to be evaluated for removal + * @return a {@literal RemoveData} object with updated old and new values after removing identifiers + */ @SuppressWarnings("unchecked") private RemoveData evaluateRemoval(List oldValues, List newValues ) { diff --git a/core/src/main/java/org/bsc/langgraph4j/state/RemoveByHash.java b/core/src/main/java/org/bsc/langgraph4j/state/RemoveByHash.java index 9283bcc..0cf420f 100644 --- a/core/src/main/java/org/bsc/langgraph4j/state/RemoveByHash.java +++ b/core/src/main/java/org/bsc/langgraph4j/state/RemoveByHash.java @@ -2,13 +2,33 @@ import java.util.Objects; +/** + * Represents a record that implements the {@link AppenderChannel.RemoveIdentifier} interface. + * + * @param the type of the value to be associated with this RemoveByHash instance + */ public record RemoveByHash(T value ) implements AppenderChannel.RemoveIdentifier { + /** + * Compares the hash code of this object with another element at a specific index. + * + * @param the type parameter of the element to compare with + * @param element the element to compare with + * @param atIndex the index of the element in the context (ignored in comparison) + * @return the difference between the hash codes of this object and the given element + */ @Override public int compareTo(T element, int atIndex) { return Objects.hashCode(value) - Objects.hashCode(element); } + /** + * Creates a new {@code RemoveByHash} instance with the specified value. + * + * @param the type of the value + * @param value the value to store in the {@code RemoveByHash} + * @return a new {@code RemoveByHash} instance + */ public static RemoveByHash of ( T value ) { return new RemoveByHash<>(value); } -} +} \ No newline at end of file From 2d2b807751423dae2dbdc0fe5a54d5ccf94368d7 Mon Sep 17 00:00:00 2001 From: bsorrentino Date: Thu, 23 Jan 2025 20:16:51 +0100 Subject: [PATCH 7/7] docs: bump release to 1.2.5 --- README.md | 2 +- src/site/markdown/index.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 186b52d..4e9d9cf 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ LangGraph for Java. A library for building stateful, multi-agents applications w | Date | Release | info |--------------|----------------| --- -| Jan 22, 2025 | `1.2.4` | official release +| Jan 23, 2025 | `1.2.4` | official release ## Samples diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md index 5f5c840..736b9b3 100644 --- a/src/site/markdown/index.md +++ b/src/site/markdown/index.md @@ -59,7 +59,7 @@ LangGraph for Java. A library for building stateful, multi-agents applications w | Date | Release | info |--------------|----------------| --- -| Jan 22, 2025 | `1.2.4` | official release +| Jan 23, 2025 | `1.2.4` | official release ## Quick Start