mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-09 19:27:54 +02:00
Code review
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
# Channel Initialization in Java LangGraph
|
||||
|
||||
This document explains how channel initialization is handled in the Java implementation of LangGraph, matching Python's behavior.
|
||||
|
||||
## Current Implementation
|
||||
|
||||
### Java Implementation (Python-Compatible)
|
||||
|
||||
In the Java implementation:
|
||||
|
||||
1. **First Superstep Behavior**:
|
||||
- Only nodes that have the input channel as one of their triggers run in the first superstep
|
||||
- This matches Python's behavior for graph execution
|
||||
|
||||
2. **Channel Reading**:
|
||||
- Channels that haven't been initialized return `null` values (instead of throwing exceptions)
|
||||
- Nodes are expected to handle potentially `null` values from uninitialized channels
|
||||
|
||||
3. **Graph Execution**:
|
||||
- Subsequent supersteps only execute nodes that:
|
||||
- Subscribe to a channel that was updated
|
||||
- OR have a trigger matching a channel that was updated
|
||||
|
||||
## Key Distinctions
|
||||
|
||||
There's an important distinction between:
|
||||
|
||||
1. **Input channels** - Channels from which the node reads values when it executes
|
||||
2. **Trigger channels** - Channels that determine when this node should execute
|
||||
|
||||
In our Java implementation:
|
||||
- `channels` property defines which channels the node reads from
|
||||
- `triggerChannels` property defines which channels can cause the node to execute
|
||||
|
||||
This naming is more intuitive and aligns better with the conceptual distinction between reading from a channel and being triggered by a channel.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
The Python-compatible implementation in Java LangGraph makes the following changes:
|
||||
|
||||
1. Modified `TaskPlanner.plan()` to only execute nodes with input channel triggers in the first superstep
|
||||
2. Updated tests to:
|
||||
- Add triggers for nodes that should execute on first superstep (e.g., `trigger("input")`)
|
||||
- Remove unnecessary manual channel initialization that was previously used to avoid EmptyChannelException
|
||||
- Use input maps to provide initial values instead of `channel.update()`
|
||||
- Only keep manual initialization in specific test cases that need it (like the mix of initialized/uninitialized channels test)
|
||||
3. Clarified the distinction between "subscribe to read" and "trigger to execute" semantics
|
||||
|
||||
## Recommended Practices
|
||||
|
||||
When building graphs with the Java implementation, follow these practices to ensure Python compatibility:
|
||||
|
||||
### 1. Add trigger channels to nodes
|
||||
|
||||
Always add appropriate trigger channels to nodes that should execute in the first superstep:
|
||||
|
||||
```java
|
||||
PregelNode node = new PregelNode.Builder("node", executable)
|
||||
.channel("input") // Channel to read from
|
||||
.triggerChannel("input") // Channel that triggers execution
|
||||
.writer("output")
|
||||
.build();
|
||||
```
|
||||
|
||||
You can also add multiple trigger channels if needed:
|
||||
|
||||
```java
|
||||
PregelNode node = new PregelNode.Builder("node", executable)
|
||||
.channel("input1")
|
||||
.channel("input2")
|
||||
.triggerChannel("input1") // Will trigger on this channel
|
||||
.triggerChannel("input2") // And also on this channel
|
||||
.writer("output")
|
||||
.build();
|
||||
```
|
||||
|
||||
### 2. Handle uninitialized channels gracefully
|
||||
|
||||
Inside node execution logic, handle potentially uninitialized channels using default values:
|
||||
|
||||
```java
|
||||
// Handle uninitialized channels with a default value
|
||||
Integer input = 0; // Default value for uninitialized channel
|
||||
if (inputs.containsKey("inputChannel") && inputs.get("inputChannel") != null) {
|
||||
input = (Integer) inputs.get("inputChannel");
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Provide initial values through input map
|
||||
|
||||
Instead of manually initializing channels, provide initial values through the input map:
|
||||
|
||||
```java
|
||||
// DO NOT do this:
|
||||
// channel.update(Collections.singletonList(initialValue));
|
||||
|
||||
// Instead, provide values in the input map:
|
||||
Map<String, Object> input = new HashMap<>();
|
||||
input.put("inputChannel", initialValue);
|
||||
Object result = pregel.invoke(input, null);
|
||||
```
|
||||
|
||||
### 4. Remember execution rules
|
||||
|
||||
- Only nodes with input channel as a trigger run in the first superstep
|
||||
- In subsequent supersteps, nodes run if they subscribe to or have a trigger matching an updated channel
|
||||
- Uninitialized channels return `null` or empty collections rather than throwing exceptions
|
||||
Reference in New Issue
Block a user