A library for building stateful, multi-agents applications with LLMs
Configure graph compilation and execution behavior with interrupts, thread management, metadata handling, and runtime control options.
Configure how graphs are compiled and prepared for execution.
/**
* Configuration for graph compilation behavior
*/
class CompileConfig {
/**
* Creates new configuration builder
* @return Builder instance for configuration
*/
static Builder builder();
/**
* Creates builder from existing configuration
* @param config Base configuration to copy
* @return Builder with copied configuration
*/
static Builder builder(CompileConfig config);
/**
* Get configured checkpoint saver
* @return Optional containing checkpoint saver if configured
*/
Optional<BaseCheckpointSaver> checkpointSaver();
/**
* Get nodes where execution should interrupt before execution
* @return Unmodifiable set of node IDs for before-interruption
*/
Set<String> interruptsBefore();
/**
* Get nodes where execution should interrupt after execution
* @return Unmodifiable set of node IDs for after-interruption
*/
Set<String> interruptsAfter();
/**
* Check if threads should be released after execution
* @return true if thread release is enabled
*/
boolean releaseThread();
/**
* Check if interruption should occur before evaluating conditional edges
* @return true if edge interruption is enabled
*/
boolean interruptBeforeEdge();
}Usage Examples:
// Basic compilation configuration
CompileConfig basicConfig = CompileConfig.builder()
.checkpointSaver(new MemorySaver())
.build();
// Advanced configuration with interruptions
CompileConfig advancedConfig = CompileConfig.builder()
.checkpointSaver(new FileSystemSaver(Paths.get("/app/checkpoints")))
.interruptBefore("human_review", "approval_step")
.interruptAfter("critical_operation", "data_validation")
.releaseThread(true)
.interruptBeforeEdge(true)
.build();
// Copy and modify existing configuration
CompileConfig modifiedConfig = CompileConfig.builder(advancedConfig)
.interruptBefore("additional_node")
.build();
// Compile graph with configuration
CompiledGraph<MyState> app = workflow.compile(advancedConfig);Fluent builder for constructing compile configurations.
/**
* Builder for CompileConfig instances
*/
class CompileConfig.Builder {
/**
* Sets checkpoint saver for persistent state
* @param checkpointSaver Checkpoint saver implementation
* @return Builder for method chaining
*/
Builder checkpointSaver(BaseCheckpointSaver checkpointSaver);
/**
* Sets nodes to interrupt before execution
* @param interruptBefore Node IDs to interrupt before
* @return Builder for method chaining
*/
Builder interruptBefore(String... interruptBefore);
/**
* Sets nodes to interrupt after execution
* @param interruptAfter Node IDs to interrupt after
* @return Builder for method chaining
*/
Builder interruptAfter(String... interruptAfter);
/**
* Sets collection of nodes to interrupt before execution
* @param interruptsBefore Collection of node IDs
* @return Builder for method chaining
*/
Builder interruptsBefore(Collection<String> interruptsBefore);
/**
* Sets collection of nodes to interrupt after execution
* @param interruptsAfter Collection of node IDs
* @return Builder for method chaining
*/
Builder interruptsAfter(Collection<String> interruptsAfter);
/**
* Enables/disables thread release after execution
* @param releaseThread Whether to release threads
* @return Builder for method chaining
*/
Builder releaseThread(boolean releaseThread);
/**
* Configures interruption before evaluating conditional edges
* @param interruptBeforeEdge Whether to interrupt before edges
* @return Builder for method chaining
*/
Builder interruptBeforeEdge(boolean interruptBeforeEdge);
/**
* Builds the configuration
* @return Configured CompileConfig instance
*/
CompileConfig build();
}Usage Examples:
// Step-by-step configuration
CompileConfig.Builder builder = CompileConfig.builder();
builder.checkpointSaver(new MemorySaver());
builder.interruptBefore("review_step", "approval_gate");
builder.interruptAfter("data_processing");
builder.releaseThread(true);
CompileConfig config = builder.build();
// Fluent configuration
CompileConfig fluentConfig = CompileConfig.builder()
.checkpointSaver(new FileSystemSaver(Paths.get("/tmp/checkpoints")))
.interruptBefore("human_input")
.interruptAfter("critical_step")
.interruptBeforeEdge(false)
.releaseThread(false)
.build();
// Configuration with collections
Set<String> beforeNodes = Set.of("node1", "node2", "node3");
Set<String> afterNodes = Set.of("nodeA", "nodeB");
CompileConfig collectionConfig = CompileConfig.builder()
.interruptsBefore(beforeNodes)
.interruptsAfter(afterNodes)
.build();Configure individual graph execution instances with thread management and metadata.
/**
* Configuration for individual graph execution runs
*/
final class RunnableConfig implements HasMetadata {
/**
* Reserved metadata key for studio environment detection
*/
static final String STUDIO_METADATA_KEY = "__STUDIO_MDK__";
/**
* Creates new configuration builder
* @return Builder instance
*/
static Builder builder();
/**
* Creates builder from existing configuration
* @param config Base configuration to copy
* @return Builder with copied configuration
*/
static Builder builder(RunnableConfig config);
/**
* Get thread identifier for this execution
* @return Optional containing thread ID if set
*/
Optional<String> threadId();
/**
* Get checkpoint identifier for resumption
* @return Optional containing checkpoint ID if set
*/
Optional<String> checkPointId();
/**
* Get next node identifier for directed execution
* @return Optional containing next node ID if set
*/
Optional<String> nextNode();
/**
* Get stream mode for execution output
* @return Stream mode (VALUES or SNAPSHOTS)
*/
CompiledGraph.StreamMode streamMode();
/**
* Get metadata value by key
* @param key Metadata key
* @return Optional containing metadata value
*/
Optional<Object> metadata(String key);
/**
* Check if execution is running in studio environment
* @return true if studio environment detected
*/
boolean isRunningInStudio();
/**
* Create new configuration with different stream mode
* @param streamMode New stream mode
* @return New configuration instance
*/
RunnableConfig withStreamMode(CompiledGraph.StreamMode streamMode);
/**
* Create new configuration with different checkpoint ID
* @param checkPointId New checkpoint ID
* @return New configuration instance
*/
RunnableConfig withCheckPointId(String checkPointId);
}Usage Examples:
// Basic runtime configuration
RunnableConfig basicRunConfig = RunnableConfig.builder()
.threadId("user-session-123")
.build();
// Advanced configuration with metadata
RunnableConfig advancedRunConfig = RunnableConfig.builder()
.threadId("advanced-session")
.checkPointId("checkpoint-abc123")
.streamMode(CompiledGraph.StreamMode.SNAPSHOTS)
.addMetadata("user_id", "user123")
.addMetadata("session_type", "interactive")
.build();
// Use in graph execution
Optional<MyState> result = app.invoke(Map.of("input", "data"), basicRunConfig);
// Check metadata
String userId = advancedRunConfig.metadata("user_id")
.map(Object::toString)
.orElse("anonymous");
// Modify configuration
RunnableConfig snapshotConfig = basicRunConfig.withStreamMode(
CompiledGraph.StreamMode.SNAPSHOTS
);Fluent builder for runtime configurations with metadata support.
/**
* Builder for RunnableConfig instances with metadata support
*/
class RunnableConfig.Builder extends HasMetadata.Builder<RunnableConfig.Builder> {
/**
* Sets thread identifier
* @param threadId Thread ID for execution
* @return Builder for method chaining
*/
Builder threadId(String threadId);
/**
* Sets checkpoint identifier for resumption
* @param checkPointId Checkpoint ID
* @return Builder for method chaining
*/
Builder checkPointId(String checkPointId);
/**
* Sets next node for directed execution
* @param nextNode Next node ID
* @return Builder for method chaining
*/
Builder nextNode(String nextNode);
/**
* Sets stream mode for output
* @param streamMode Stream mode (VALUES or SNAPSHOTS)
* @return Builder for method chaining
*/
Builder streamMode(CompiledGraph.StreamMode streamMode);
/**
* Adds custom executor for parallel node
* @param nodeId Parallel node ID
* @param executor Executor for parallel execution
* @return Builder for method chaining
*/
Builder addParallelNodeExecutor(String nodeId, Executor executor);
/**
* Builds the configuration
* @return Configured RunnableConfig instance
*/
RunnableConfig build();
}Usage Examples:
import java.util.concurrent.Executors;
// Step-by-step configuration
RunnableConfig.Builder builder = RunnableConfig.builder();
builder.threadId("session-456");
builder.streamMode(CompiledGraph.StreamMode.VALUES);
builder.addMetadata("priority", "high");
builder.addMetadata("timeout", 30000);
RunnableConfig config = builder.build();
// Parallel execution configuration
RunnableConfig parallelConfig = RunnableConfig.builder()
.threadId("parallel-session")
.addParallelNodeExecutor("data_processor", Executors.newFixedThreadPool(4))
.addParallelNodeExecutor("file_handler", Executors.newCachedThreadPool())
.build();
// Resumption configuration
RunnableConfig resumeConfig = RunnableConfig.builder()
.threadId("interrupted-session")
.checkPointId("checkpoint-xyz789")
.nextNode("resume_point")
.build();Handle custom metadata for execution context and configuration.
/**
* Interface for objects that can hold metadata
*/
interface HasMetadata {
/**
* Get metadata value by key
* @param key Metadata key
* @return Optional containing metadata value
*/
Optional<Object> metadata(String key);
/**
* Get typed metadata value by key
* @param key Metadata key
* @param typeRef Type reference for casting
* @return Optional containing typed metadata value
*/
default <T> Optional<T> metadata(String key, TypeRef<T> typeRef) {
return metadata(key).flatMap(typeRef::cast);
}
/**
* Base builder class for metadata-aware objects
*/
abstract class Builder<T extends Builder<T>> {
/**
* Add metadata key-value pair
* @param key Metadata key
* @param value Metadata value
* @return Builder for method chaining
*/
T addMetadata(String key, Object value);
/**
* Get current metadata map
* @return Current metadata
*/
Map<String, Object> metadata();
}
}Usage Examples:
// Add various metadata types
RunnableConfig configWithMetadata = RunnableConfig.builder()
.threadId("metadata-session")
.addMetadata("user_id", "user123")
.addMetadata("priority", 5)
.addMetadata("timeout", Duration.ofMinutes(10))
.addMetadata("features", List.of("feature1", "feature2"))
.addMetadata("config", Map.of("debug", true, "verbose", false))
.build();
// Access metadata in actions
AsyncNodeActionWithConfig<MyState> metadataAwareAction = (state, config) -> {
// Get user ID
String userId = config.metadata("user_id")
.map(Object::toString)
.orElse("anonymous");
// Get priority with type safety
Optional<Integer> priority = config.metadata("priority", new TypeRef<Integer>() {});
// Get features list
Optional<List<String>> features = config.metadata("features", new TypeRef<List<String>>() {});
Map<String, Object> updates = new HashMap<>();
updates.put("user_id", userId);
priority.ifPresent(p -> updates.put("priority_level", p));
features.ifPresent(f -> updates.put("enabled_features", f));
return CompletableFuture.completedFuture(updates);
};Control how execution outputs are delivered.
/**
* Stream modes for execution output
*/
enum CompiledGraph.StreamMode {
/**
* Stream node execution outputs with state
*/
VALUES,
/**
* Stream state snapshots with checkpoint information
*/
SNAPSHOTS
}Usage Examples:
// Configuration for different stream modes
RunnableConfig valuesConfig = RunnableConfig.builder()
.threadId("values-session")
.streamMode(CompiledGraph.StreamMode.VALUES)
.build();
RunnableConfig snapshotsConfig = RunnableConfig.builder()
.threadId("snapshots-session")
.streamMode(CompiledGraph.StreamMode.SNAPSHOTS)
.build();
// Stream with VALUES mode
AsyncGenerator<NodeOutput<MyState>> valuesStream = app.stream(
Map.of("input", "data"),
valuesConfig
);
valuesStream.forEachAsync(output -> {
System.out.println("Node: " + output.node());
System.out.println("State: " + output.state().data());
return CompletableFuture.completedFuture(null);
});
// Stream with SNAPSHOTS mode
AsyncGenerator<NodeOutput<MyState>> snapshotsStream = app.stream(
Map.of("input", "data"),
snapshotsConfig
);
snapshotsStream.forEachAsync(output -> {
if (output instanceof StateSnapshot) {
StateSnapshot<MyState> snapshot = (StateSnapshot<MyState>) output;
System.out.println("Checkpoint: " + snapshot.getCheckpointId());
System.out.println("Node: " + snapshot.getNodeId());
System.out.println("Next: " + snapshot.getNextNodeId());
}
return CompletableFuture.completedFuture(null);
});Different configurations for different environments.
// Development configuration with debugging
CompileConfig developmentConfig = CompileConfig.builder()
.checkpointSaver(new MemorySaver())
.interruptBefore("debug_point")
.releaseThread(false) // Keep threads for debugging
.build();
// Production configuration with persistence
CompileConfig productionConfig = CompileConfig.builder()
.checkpointSaver(new FileSystemSaver(Paths.get("/var/app/checkpoints")))
.releaseThread(true) // Release threads to save memory
.interruptBeforeEdge(false) // No debugging interrupts
.build();
// Environment-based configuration
boolean isProduction = "production".equals(System.getenv("ENVIRONMENT"));
CompileConfig config = isProduction ? productionConfig : developmentConfig;Configure execution for different user contexts.
// User-specific configuration factory
public class UserConfigFactory {
public static RunnableConfig createUserConfig(String userId, UserRole role) {
RunnableConfig.Builder builder = RunnableConfig.builder()
.threadId("user-" + userId)
.addMetadata("user_id", userId)
.addMetadata("role", role.name())
.addMetadata("session_start", System.currentTimeMillis());
// Add role-specific configuration
switch (role) {
case ADMIN:
builder.addMetadata("permissions", Set.of("read", "write", "delete"));
builder.streamMode(CompiledGraph.StreamMode.SNAPSHOTS); // Debug info
break;
case USER:
builder.addMetadata("permissions", Set.of("read"));
builder.streamMode(CompiledGraph.StreamMode.VALUES);
break;
}
return builder.build();
}
}
// Usage
RunnableConfig adminConfig = UserConfigFactory.createUserConfig("admin123", UserRole.ADMIN);
RunnableConfig userConfig = UserConfigFactory.createUserConfig("user456", UserRole.USER);Configure interruptions based on runtime conditions.
// Conditional interruption helper
public class ConditionalInterrupts {
public static CompileConfig createConditionalConfig(boolean debugMode, boolean requireApproval) {
CompileConfig.Builder builder = CompileConfig.builder()
.checkpointSaver(new MemorySaver());
if (debugMode) {
builder.interruptBefore("validation", "processing", "output");
builder.interruptBeforeEdge(true);
}
if (requireApproval) {
builder.interruptAfter("user_input", "data_modification");
}
return builder.build();
}
}
// Usage based on user preferences
boolean debugEnabled = user.getPreferences().isDebugEnabled();
boolean approvalRequired = user.hasRole("REQUIRES_APPROVAL");
CompileConfig config = ConditionalInterrupts.createConditionalConfig(debugEnabled, approvalRequired);
CompiledGraph<MyState> app = workflow.compile(config);Install with Tessl CLI
npx tessl i tessl/maven-org-bsc-langgraph4j--langgraph4j-core