# Java Pregel Core Interfaces This document defines the Java interfaces for the Pregel execution engine, the computational backbone of LangGraph. ## `PregelProtocol` Interface The main interface defining the contract for Pregel implementations. ```java package com.langgraph.pregel; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; /** * Interface defining the contract for all Pregel implementations. */ public interface PregelProtocol { /** * Invoke the graph with input. * * @param input Input to the graph * @param config Optional configuration * @return Output from the graph */ Object invoke(Object input, Map config); /** * Stream execution results. * * @param input Input to the graph * @param config Optional configuration * @param streamMode Mode of streaming * @return Iterator of execution updates */ Iterator stream(Object input, Map config, StreamMode streamMode); /** * Get the current state. * * @param threadId Optional thread ID * @return Current state */ Object getState(String threadId); /** * Update the state. * * @param threadId Thread ID * @param state New state */ void updateState(String threadId, Object state); /** * Get the state history. * * @param threadId Thread ID * @return List of state snapshots */ List getStateHistory(String threadId); } ``` ## `StreamMode` Enum Defines the different streaming options. ```java package com.langgraph.pregel; /** * Enum defining the different streaming options. */ public enum StreamMode { /** * Stream the complete state after each superstep. */ VALUES, /** * Stream state deltas after each node execution. */ UPDATES, /** * Stream comprehensive execution information for debugging. */ DEBUG } ``` ## `PregelExecutable` Interface Interface for actions that can be executed within Pregel. ```java package com.langgraph.pregel; import java.util.Map; /** * Interface for actions that can be executed within Pregel. */ @FunctionalInterface public interface PregelExecutable { /** * Execute the action. * * @param inputs Channel inputs * @param context Execution context * @return Map of channel updates */ Map execute(Map inputs, Map context); } ``` ## `PregelNode` Class Represents an actor in the Pregel system. ```java package com.langgraph.pregel; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Set; /** * Represents an actor in the Pregel system. */ public class PregelNode { private final String name; private final PregelExecutable action; private final Set subscribe; private final String trigger; private final Set writers; private final RetryPolicy retryPolicy; /** * Create a PregelNode. * * @param name Unique identifier for the node * @param action Function to execute when the node is triggered * @param subscribe Channel names this node listens to for updates * @param trigger Special condition for node execution * @param writers Channels this node can write to (for validation) * @param retryPolicy Strategy for handling execution failures */ public PregelNode( String name, PregelExecutable action, Collection subscribe, String trigger, Collection writers, RetryPolicy retryPolicy) { this.name = name; this.action = action; this.subscribe = subscribe != null ? new HashSet<>(subscribe) : Collections.emptySet(); this.trigger = trigger; this.writers = writers != null ? new HashSet<>(writers) : Collections.emptySet(); this.retryPolicy = retryPolicy; } /** * Create a PregelNode with default values. * * @param name Unique identifier for the node * @param action Function to execute when the node is triggered */ public PregelNode(String name, PregelExecutable action) { this(name, action, null, null, null, null); } /** * Get the name of the node. * * @return Node name */ public String getName() { return name; } /** * Get the action to execute. * * @return Node action */ public PregelExecutable getAction() { return action; } /** * Get the channels this node subscribes to. * * @return Set of channel names */ public Set getSubscribe() { return Collections.unmodifiableSet(subscribe); } /** * Get the trigger condition for this node. * * @return Trigger condition */ public String getTrigger() { return trigger; } /** * Get the channels this node can write to. * * @return Set of channel names */ public Set getWriters() { return Collections.unmodifiableSet(writers); } /** * Get the retry policy for this node. * * @return Retry policy */ public RetryPolicy getRetryPolicy() { return retryPolicy; } /** * Check if this node subscribes to a specific channel. * * @param channelName Channel name to check * @return True if the node subscribes to the channel */ public boolean subscribesTo(String channelName) { return subscribe.contains(channelName); } /** * Check if this node has a specific trigger. * * @param triggerName Trigger name to check * @return True if the node has the trigger */ public boolean hasTrigger(String triggerName) { return trigger != null && trigger.equals(triggerName); } /** * Check if this node can write to a specific channel. * * @param channelName Channel name to check * @return True if the node can write to the channel */ public boolean canWriteTo(String channelName) { return writers.contains(channelName); } } ``` ## `PregelTask` and `PregelExecutableTask` Classes Units of work representing computations to execute. ```java package com.langgraph.pregel; import java.util.Collections; import java.util.Map; import java.util.Objects; /** * Represents a task to be executed within Pregel. */ public class PregelTask { private final String node; private final String trigger; private final RetryPolicy retryPolicy; /** * Create a PregelTask. * * @param node Node name * @param trigger Optional trigger * @param retryPolicy Optional retry policy */ public PregelTask(String node, String trigger, RetryPolicy retryPolicy) { this.node = node; this.trigger = trigger; this.retryPolicy = retryPolicy; } /** * Create a PregelTask with default values. * * @param node Node name */ public PregelTask(String node) { this(node, null, null); } /** * Get the node name. * * @return Node name */ public String getNode() { return node; } /** * Get the trigger. * * @return Trigger */ public String getTrigger() { return trigger; } /** * Get the retry policy. * * @return Retry policy */ public RetryPolicy getRetryPolicy() { return retryPolicy; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PregelTask that = (PregelTask) o; return Objects.equals(node, that.node) && Objects.equals(trigger, that.trigger); } @Override public int hashCode() { return Objects.hash(node, trigger); } } /** * Represents an executable task with inputs and context. */ public class PregelExecutableTask { private final PregelTask task; private final Map inputs; private final Map context; /** * Create a PregelExecutableTask. * * @param task Task to execute * @param inputs Channel inputs * @param context Execution context */ public PregelExecutableTask( PregelTask task, Map inputs, Map context) { this.task = task; this.inputs = inputs != null ? inputs : Collections.emptyMap(); this.context = context != null ? context : Collections.emptyMap(); } /** * Get the task. * * @return Task */ public PregelTask getTask() { return task; } /** * Get the inputs. * * @return Map of channel inputs */ public Map getInputs() { return Collections.unmodifiableMap(inputs); } /** * Get the context. * * @return Map of context values */ public Map getContext() { return Collections.unmodifiableMap(context); } } ``` ## `RetryPolicy` Interface Interface for handling execution failures. ```java package com.langgraph.pregel; /** * Interface for handling execution failures. */ public interface RetryPolicy { /** * Decide how to handle a failed execution. * * @param attempt Current attempt number (1-based) * @param error Error that occurred * @return Retry decision */ RetryDecision shouldRetry(int attempt, Throwable error); /** * Enum defining retry decisions. */ enum RetryDecision { /** * Retry the execution */ RETRY, /** * Fail the execution */ FAIL } /** * Create a simple retry policy with a maximum number of attempts. * * @param maxAttempts Maximum number of attempts * @return Retry policy */ static RetryPolicy maxAttempts(int maxAttempts) { return (attempt, error) -> attempt < maxAttempts ? RetryDecision.RETRY : RetryDecision.FAIL; } /** * Create a retry policy that never retries. * * @return Retry policy */ static RetryPolicy noRetry() { return (attempt, error) -> RetryDecision.FAIL; } /** * Create a retry policy that always retries. * * @return Retry policy */ static RetryPolicy alwaysRetry() { return (attempt, error) -> RetryDecision.RETRY; } } ``` ## `Checkpoint` Class Represents a snapshot of execution state at a superstep boundary. ```java package com.langgraph.pregel; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * Represents a snapshot of execution state at a superstep boundary. */ public class Checkpoint { private Map channelValues; /** * Create a Checkpoint. * * @param channelValues Channel values */ public Checkpoint(Map channelValues) { this.channelValues = new HashMap<>(channelValues); } /** * Get the channel values. * * @return Map of channel values */ public Map getValues() { return Collections.unmodifiableMap(channelValues); } /** * Update the channel values. * * @param channelValues New channel values */ public void update(Map channelValues) { this.channelValues = new HashMap<>(channelValues); } } ``` ## `Pregel` Class (Core Implementation) The central implementation of the Pregel execution engine. ```java package com.langgraph.pregel; import com.langgraph.channels.Channel; import com.langgraph.checkpoint.base.BaseCheckpointSaver; import java.util.*; import java.util.concurrent.*; import java.util.function.Function; import java.util.stream.Collectors; /** * Implementation of the Pregel execution engine. */ public class Pregel implements PregelProtocol { private final Map nodes; private final Map channels; private final BaseCheckpointSaver checkpointer; private final ExecutorService executor; /** * Create a Pregel instance. * * @param nodes Map of node names to nodes * @param channels Map of channel names to channels * @param checkpointer Optional checkpointer */ public Pregel( Map nodes, Map channels, BaseCheckpointSaver checkpointer) { this.nodes = new HashMap<>(nodes); this.channels = new HashMap<>(channels); this.checkpointer = checkpointer; this.executor = Executors.newWorkStealingPool(); } @Override public Object invoke(Object input, Map config) { // Initialize execution context String threadId = getThreadId(config); Map context = createContext(threadId, config); // Initialize or restore channel values initializeChannels(threadId, input); // Execute to completion List result = new ArrayList<>(); for (Object update : executeToCompletion(threadId, context)) { result.add(update); } // Return final state return result.isEmpty() ? null : result.get(result.size() - 1); } @Override public Iterator stream(Object input, Map config, StreamMode streamMode) { // Initialize execution context String threadId = getThreadId(config); Map context = createContext(threadId, config); // Initialize or restore channel values initializeChannels(threadId, input); // Execute and stream results return executeToCompletion(threadId, context); } @Override public Object getState(String threadId) { if (threadId == null) { throw new IllegalArgumentException("Thread ID is required"); } return captureState(); } @Override public void updateState(String threadId, Object state) { if (threadId == null) { throw new IllegalArgumentException("Thread ID is required"); } if (!(state instanceof Map)) { throw new IllegalArgumentException("State must be a Map"); } @SuppressWarnings("unchecked") Map stateMap = (Map) state; // Update channels with the state for (Map.Entry entry : stateMap.entrySet()) { String channelName = entry.getKey(); Object value = entry.getValue(); if (channels.containsKey(channelName)) { channels.get(channelName).update(value); } } // Create a checkpoint if (checkpointer != null) { checkpointer.checkpoint(threadId, captureChannelValues()); } } @Override public List getStateHistory(String threadId) { if (threadId == null) { throw new IllegalArgumentException("Thread ID is required"); } if (checkpointer == null) { return Collections.emptyList(); } List checkpoints = checkpointer.list(threadId); List history = new ArrayList<>(); for (String checkpointId : checkpoints) { Optional> values = checkpointer.getValues(checkpointId); values.ifPresent(history::add); } return history; } /** * Get the thread ID from the configuration. * * @param config Configuration * @return Thread ID */ private String getThreadId(Map config) { if (config == null || !config.containsKey("thread_id")) { return UUID.randomUUID().toString(); } return config.get("thread_id").toString(); } /** * Create the execution context. * * @param threadId Thread ID * @param config Configuration * @return Context map */ private Map createContext(String threadId, Map config) { Map context = new HashMap<>(); context.put("thread_id", threadId); if (config != null) { context.putAll(config); } return context; } /** * Initialize or restore channel values. * * @param threadId Thread ID * @param input Input to the graph */ private void initializeChannels(String threadId, Object input) { // Check for existing checkpoint if (checkpointer != null) { Optional latestCheckpoint = checkpointer.latest(threadId); if (latestCheckpoint.isPresent()) { // Restore from checkpoint Optional> values = checkpointer.getValues(latestCheckpoint.get()); if (values.isPresent()) { for (Map.Entry entry : values.get().entrySet()) { String channelName = entry.getKey(); Object value = entry.getValue(); if (channels.containsKey(channelName)) { channels.get(channelName).fromCheckpoint(value); } } return; } } } // Initialize with input if (input instanceof Map) { @SuppressWarnings("unchecked") Map inputMap = (Map) input; for (Map.Entry entry : inputMap.entrySet()) { String channelName = entry.getKey(); Object value = entry.getValue(); if (channels.containsKey(channelName)) { channels.get(channelName).update(value); } } } } /** * Execute the graph to completion. * * @param threadId Thread ID * @param context Execution context * @return Iterator of execution updates */ private Iterator executeToCompletion(String threadId, Map context) { return new Iterator() { private boolean hasMore = true; private final Set updatedChannels = new HashSet<>(); @Override public boolean hasNext() { return hasMore; } @Override public Object next() { if (!hasMore) { throw new NoSuchElementException(); } // Identify active nodes List tasks = planSuperstep(updatedChannels); if (tasks.isEmpty()) { hasMore = false; return captureState(); } // Reset updated channels for this superstep updatedChannels.clear(); // Execute all tasks executeSuperstep(tasks, context, updatedChannels); // Create checkpoint if needed if (checkpointer != null) { checkpointer.checkpoint(threadId, captureChannelValues()); } // Capture current state Object state = captureState(); // Check if we're done hasMore = !updatedChannels.isEmpty(); return state; } }; } /** * Plan which nodes to execute in the current superstep. * * @param updatedChannels Set of channel names that were updated * @return List of tasks to execute */ private List planSuperstep(Set updatedChannels) { List tasks = new ArrayList<>(); for (PregelNode node : nodes.values()) { // Check if the node subscribes to any updated channels boolean shouldExecute = false; for (String channelName : node.getSubscribe()) { if (updatedChannels.contains(channelName)) { shouldExecute = true; break; } } // Check if the node has a trigger if (node.getTrigger() != null && updatedChannels.contains(node.getTrigger())) { shouldExecute = true; } if (shouldExecute) { tasks.add(new PregelTask(node.getName(), node.getTrigger(), node.getRetryPolicy())); } } return tasks; } /** * Execute all tasks in the current superstep. * * @param tasks Tasks to execute * @param context Execution context * @param updatedChannels Set to track which channels were updated */ private void executeSuperstep( List tasks, Map context, Set updatedChannels) { // Create executable tasks List executableTasks = new ArrayList<>(); for (PregelTask task : tasks) { // Get inputs for the node Map inputs = new HashMap<>(); PregelNode node = nodes.get(task.getNode()); for (String channelName : node.getSubscribe()) { if (channels.containsKey(channelName)) { inputs.put(channelName, channels.get(channelName).getValue()); } } // Add trigger value if present if (task.getTrigger() != null && channels.containsKey(task.getTrigger())) { inputs.put(task.getTrigger(), channels.get(task.getTrigger()).getValue()); } executableTasks.add(new PregelExecutableTask(task, inputs, context)); } // Execute tasks in parallel List> results = executeTasks(executableTasks); // Apply updates for (Map updates : results) { if (updates != null) { for (Map.Entry entry : updates.entrySet()) { String channelName = entry.getKey(); Object value = entry.getValue(); if (channels.containsKey(channelName)) { boolean wasUpdated = channels.get(channelName).update(value); if (wasUpdated) { updatedChannels.add(channelName); } } } } } // Reset updated flags on channels for (Channel channel : channels.values()) { if (channel instanceof Resettable) { ((Resettable) channel).resetUpdated(); } } } /** * Execute tasks in parallel. * * @param tasks Tasks to execute * @return List of results */ private List> executeTasks(List tasks) { List>> futures = new ArrayList<>(); for (PregelExecutableTask task : tasks) { CompletableFuture> future = CompletableFuture.supplyAsync(() -> { try { PregelNode node = nodes.get(task.getTask().getNode()); return node.getAction().execute(task.getInputs(), task.getContext()); } catch (Exception e) { // Handle retry logic RetryPolicy retryPolicy = task.getTask().getRetryPolicy(); if (retryPolicy != null) { // Retry logic would be implemented here // For simplicity, we're just letting it fail } throw new RuntimeException( "Error executing node: " + task.getTask().getNode(), e); } }, executor); futures.add(future); } // Wait for all tasks to complete try { return futures.stream() .map(CompletableFuture::join) .collect(Collectors.toList()); } catch (Exception e) { // Handle task execution failures throw new RuntimeException("Error executing tasks", e); } } /** * Capture the current channel values. * * @return Map of channel values */ private Map captureChannelValues() { Map values = new HashMap<>(); for (Map.Entry entry : channels.entrySet()) { String channelName = entry.getKey(); Channel channel = entry.getValue(); Object value = channel.checkpoint(); if (value != null) { values.put(channelName, value); } } return values; } /** * Capture the current state. * * @return State map */ private Map captureState() { Map state = new HashMap<>(); for (Map.Entry entry : channels.entrySet()) { String channelName = entry.getKey(); Channel channel = entry.getValue(); Object value = channel.getValue(); if (value != null) { state.put(channelName, value); } } return state; } /** * Interface for channels that can be reset. */ private interface Resettable { void resetUpdated(); } /** * Builder for creating Pregel instances. */ public static class Builder { private final Map nodes = new HashMap<>(); private final Map channels = new HashMap<>(); private BaseCheckpointSaver checkpointer; /** * Add a node. * * @param node Node to add * @return This builder */ public Builder addNode(PregelNode node) { nodes.put(node.getName(), node); return this; } /** * Add a channel. * * @param name Channel name * @param channel Channel to add * @return This builder */ public Builder addChannel(String name, Channel channel) { channels.put(name, channel); return this; } /** * Set the checkpointer. * * @param checkpointer Checkpointer to use * @return This builder */ public Builder setCheckpointer(BaseCheckpointSaver checkpointer) { this.checkpointer = checkpointer; return this; } /** * Build the Pregel instance. * * @return Pregel instance */ public Pregel build() { return new Pregel(nodes, channels, checkpointer); } } } ```