Loading...
Loading...
Avg 90.8 stars per repo.
12 new projects with 122 stars.
54 followers.
LangGraph for Java. A library for building stateful, multi-agents applications with LLMs, built for work with langchain4j and Spring AI
It is inspired by LangGraph solution, part of LangChain AI project.
| Date | Release | info
|--------------|----------------| ---
| Feb 7, 2026 | 1.8.1 | last release
βΌοΈ Note:
The miminum supported version is the
Java 17
Welcome to LangGraph4j! This guide will help you understand the core concepts of LangGraph4j, install it, and build your first application.
LangGraph4j is a Java library for building stateful, multi-agent applications with Large Language Models (LLMs). It is inspired by the Python library LangGraph and is designed to work seamlessly with popular Java LLM frameworks like Langchain4j and Spring AI.
At its core, LangGraph4j allows you to define cyclical graphs where different components (agents, tools, or custom logic) can interact in a stateful manner. This is crucial for building complex applications that require memory, context, and the ability for different "agents" to collaborate or hand off tasks.
LangGraph4j offers several features and benefits:
Understanding these concepts is key to using LangGraph4j effectively:
StateGraph<S extends AgentState>The StateGraph is the primary class you'll use to define the structure of your application. It's where you add nodes and edges to create your graph. It is parameterized by an AgentState.
AgentStateThe AgentState (or a class extending it) represents the shared state of your graph. It's essentially a map (Map<String, Object>) that gets passed from node to node. Each node can read from this state and return updates to it.
Map<String, Channel.Reducer>. Each key in the map corresponds to an attribute in the state.Channel.Reducer: A reducer defines how updates to a state attribute are handled. For example, a new value might overwrite the old one, or it might be added to a list of existing values.Channel.Default<T>: Provides a default value for a state attribute if it's not already set.Channel.Appender<T> / MessageChannel.Appender<M>: A common type of reducer that appends the new value to a list associated with the state attribute. This is useful for accumulating messages, tool calls, or other sequences of data. MessageChannel.Appender is specifically designed for chat messages and can also handle message deletion by ID.NodesNodes are the building blocks of your graph that perform actions. A node is typically a function (or a class implementing NodeAction<S> or AsyncNodeAction<S>) that:
AgentState as input.Map<String, Object> representing updates to the state. These updates are then applied to the AgentState according to the schema's reducers.Nodes can be synchronous or asynchronous (CompletableFuture).
EdgesEdges define the flow of control between nodes.
addEdge(sourceNodeName, destinationNodeName).AgentState. After a source node completes, an EdgeAction<S> (or AsyncEdgeAction<S>) function is executed. This function receives the current state and returns the name of the next node to execute. This allows for branching logic (e.g., if an agent decided to use a tool, go to the "execute_tool" node; otherwise, go to the "respond_to_user" node). Conditional edges are defined with addConditionalEdges(...).addConditionalEntryPoint(...).CompilationOnce you've defined all your nodes and edges in a StateGraph, you compile() it into a CompiledGraph<S extends AgentState>. This compiled graph is an immutable, runnable representation of your logic. Compilation validates the graph structure (e.g., checks for orphaned nodes).
Checkpoints (Persistence)LangGraph4j allows you to save (Checkpoint) the state of your graph at any step. This is extremely useful for:
CheckpointSaver implementation (e.g., MemorySaver for in-memory storage, or you can implement your own for persistent storage).langgraph4j/
βββ langgraph4j-bom/ # LangGraph4j dependency management
βββ langgraph4j-core/ # LangGraph4j core components
βββ langgraph4j-opentelemetry/ # LangGraph4j Hook & OpenTelemetry integration module
βββ langgraph4j-mysql-saver # LangGraph4j persistent checkpoint saver based on MySQL
βββ langgraph4j-oracle-saver # LangGraph4j persistent checkpoint saver based on OracleDB
βββ langgraph4j-postgres-saver # LangGraph4j persistent checkpoint saver based on PostgresSQL
βββ langchain4j/ # LangChain4j integration
β βββ langchain4j-core/ # LangChain4j core components (integration required)
β βββ langchain4j-agent/ # LangChain4j agent executor
βββ spring-ai/ # Spring AI integration
β βββ spring-ai-core/ # Spring AI core components (integration required)
β βββ spring-ai-agent/ # Spring AI agent executor
βββ studio/ # LangGraph4j Studio (web UI)
β βββ base/ # Base classes and interfaces
β βββ jetty/ # Jetty server implementation
β βββ quarkus/ # Quarkus server implementation
β βββ springboot/ # Spring Boot implementation
βββ how-tos/ # How-tos and examples
βββ javelit/ # LangGraph4j & Javelit
To use LangGraph4j in your project, you need to add it as a dependency.
Maven:
Make sure you are using Java 17 or later.
Latest Stable Version (Recommended):
<properties>
<langgraph4j.version>1.8.1</langgraph4j.version> <!-- Check for the actual latest version -->
</properties>
<!-- Optional: Add the Bill of Materials (BOM) to manage langgraph4j module versions -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.bsc.langgraph4j</groupId>
<artifactId>langgraph4j-bom</artifactId>
<version>${langgraph4j.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.bsc.langgraph4j</groupId>
<artifactId>langgraph4j-core</artifactId>
</dependency>
<!-- Add other langgraph4j modules if needed, e.g., langgraph4j-langchain4j -->
</dependencies>
(Note: Always check the Maven Central Repository for the latest version number.)
Development Snapshot Version: If you want to use the latest unreleased features, you can use a snapshot version.
<dependency>
<groupId>org.bsc.langgraph4j</groupId>
<artifactId>langgraph4j-core</artifactId>
<version>1.8-SNAPSHOT</version> <!-- Or the current snapshot version -->
</dependency>
You might need to configure your settings.xml or pom.xml to include the Sonatype OSS snapshots repository:
<repositories>
<repository>
<id>sonatype-oss-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
Let's create a very simple graph that has two nodes: greeter and responder.
The greeter node will add a greeting message to the state.
The responder node will add a response message based on the greeting.
1. Define the State: Our state will hold a list of messages.
import org.bsc.langgraph4j.state.AgentState;
import org.bsc.langgraph4j.state.Channels;
import org.bsc.langgraph4j.state.Channel;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
// Define the state for our graph
class SimpleState extends AgentState {
public static final String MESSAGES_KEY = "messages";
// Define the schema for the state.
// MESSAGES_KEY will hold a list of strings, and new messages will be appended.
public static final Map<String, Channel<?>> SCHEMA = Map.of(
MESSAGES_KEY, Channels.appender(ArrayList::new)
);
public SimpleState(Map<String, Object> initData) {
super(initData);
}
public List<String> messages() {
return this.<List<String>>value("messages")
.orElse( List.of() );
}
}
2. Define the Nodes:
import org.bsc.langgraph4j.action.NodeAction;
import java.util.List;
import java.util.Map;
// Node that adds a greeting
class GreeterNode implements NodeAction<SimpleState> {
@Override
public Map<String, Object> apply(SimpleState state) {
System.out.println("GreeterNode executing. Current messages: " + state.messages());
return Map.of(SimpleState.MESSAGES_KEY, "Hello from GreeterNode!");
}
}
// Node that adds a response
class ResponderNode implements NodeAction<SimpleState> {
@Override
public Map<String, Object> apply(SimpleState state) {
System.out.println("ResponderNode executing. Current messages: " + state.messages());
List<String> currentMessages = state.messages();
if (currentMessages.contains("Hello from GreeterNode!")) {
return Map.of(SimpleState.MESSAGES_KEY, "Acknowledged greeting!");
}
return Map.of(SimpleState.MESSAGES_KEY, "No greeting found.");
}
}
3. Define and Compile the Graph:
import org.bsc.langgraph4j.StateGraph;
import org.bsc.langgraph4j.GraphStateException;
import static org.bsc.langgraph4j.action.AsyncNodeAction.node_async;
import static org.bsc.langgraph4j.StateGraph.START;
import static org.bsc.langgraph4j.StateGraph.END;
import java.util.Map;
public class SimpleGraphApp {
public static void main(String[] args) throws GraphStateException {
// Initialize nodes
GreeterNode greeterNode = new GreeterNode();
ResponderNode responderNode = new ResponderNode();
// Define the graph structure
var stateGraph = new StateGraph<>(SimpleState.SCHEMA, initData -> new SimpleState(initData))
.addNode("greeter", node_async(greeterNode))
.addNode("responder", node_async(responderNode))
// Define edges
.addEdge(START, "greeter") // Start with the greeter node
.addEdge("greeter", "responder")
.addEdge("responder", END) // End after the responder node
;
// Compile the graph
var compiledGraph = stateGraph.compile();
// Run the graph
// The `stream` method returns an AsyncGenerator.
// For simplicity, we'll collect results. In a real app, you might process them as they arrive.
// Here, the final state after execution is the item of interest.
for (var item : compiledGraph.stream( Map.of( SimpleState.MESSAGES_KEY, "Let's, begin!" ) ) ) {
System.out.println( item );
}
}
}
Explanation:
SimpleState with a MESSAGES_KEY that uses AppenderChannel to accumulate strings.GreeterNode adds a "Hello" message.ResponderNode checks for the greeting and adds an acknowledgment.StateGraph is defined, nodes are added, and edges specify the flow: START -> greeter -> responder -> END.stateGraph.compile() creates the runnable CompiledGraph.compiledGraph.stream(initialState) executes the graph. We iterate through the stream to get the final state. Each item in the stream represents the state after a node has executed.This example demonstrates the basic workflow: define state, define nodes, wire them with edges, compile, and run.
As shown in the example, you typically run a compiled graph using one of its execution methods:
stream(S initialState, RunnableConfig config): Executes the graph and returns an AsyncGenerator<S>. Each yielded item is the state S after a node has completed. This is useful for observing the state at each step or for streaming partial results.invoke(S initialState, RunnableConfig config): Executes the graph and returns a CompletableFuture<S> that completes with the final state of the graph after it reaches an END node.The RunnableConfig can be used to pass in runtime configuration.
Langgraph4j Studio is an embeddable web application for visualizing and experimenting with graphs:
To explore the Langgraph4j Studio go to studio
As default use case to proof LangChain4j integration, We have implemented AgentExecutor (aka ReACT Agent) using LangGraph4j. In the project's module, you can the complete working code with tests. Feel free to checkout and use it as a reference.
Below usage example of the AgentExecutor.
public class TestTool {
@Tool("tool for test AI agent executor")
String execTest(@P("test message") String message) {
return format( "test tool ('%s') executed with result 'OK'", message);
}
@Tool("return current number of system thread allocated by application")
int threadCount() {
return Thread.getAllStackTraces().size();
}
}
var model = OllamaChatModel.builder()
.modelName( "qwen2.5:7b" )
.baseUrl("http://localhost:11434")
.supportedCapabilities(Capability.RESPONSE_FORMAT_JSON_SCHEMA)
.logRequests(true)
.logResponses(true)
.maxRetries(2)
.temperature(0.0)
.build();
var agent = AgentExecutor.builder()
.chatModel(model)
.toolsFromObject(new TestTool())
.build()
.compile();
for (var item : agent.stream( Map.of( "messages", "perform test twice and return number of current active threads" ) ) ) {
System.out.println( item );
}
As default use case to proof Spring AI integration, We have implemented AgentExecutor (aka ReACT Agent) using LangGraph4j. In the project's module, you can the complete working code with tests. Feel free to checkout and use it as a reference.
Below usage example of the AgentExecutor.
public class TestTool {
@Tool(description = "tool for test AI agent executor")
String execTest( @ToolParam(description ="test message") String message ) {
return format( "test tool ('%s') executed with result 'OK'", message);
}
@Tool(description = "return current number of system thread allocated by application")
int threadCount() {
return Thread.getAllStackTraces().size();
}
}
var model = OllamaChatModel.builder()
.ollamaApi(OllamaApi.builder().baseUrl("http://localhost:11434").build())
.defaultOptions(OllamaOptions.builder()
.model("qwen2.5:7b")
.temperature(0.1)
.build())
.build();
var agent = AgentExecutor.builder()
.chatModel(model)
.toolsFromObject(new TestTool())
.build()
.compile()
;
for (var item : agent.stream( Map.of( "messages", "perform test twice and return number of current active threads" ) ) ) {
System.out.println( item );
}
LangGraph4j is packed with features to build sophisticated agentic applications:
CompletableFuture), allowing for non-blocking I/O operations, especially when dealing with LLM calls.Now that you have a basic understanding of LangGraph4j, here's how you can continue your journey:
how-to: The how-tos/ directory in the repository contains Jupyter notebooks (runnable with Java kernels like IJava) that demonstrate various features with code examples.We hope this guide helps you get started with LangGraph4j. Happy building!