Skip to content

Commit

Permalink
Merge branch 'release/1.2.5'
Browse files Browse the repository at this point in the history
  • Loading branch information
bsorrentino committed Jan 23, 2025
2 parents 0c0e931 + 2d2b807 commit 9c53762
Show file tree
Hide file tree
Showing 16 changed files with 330 additions and 102 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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]_)
Expand All @@ -43,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
Expand Down
2 changes: 1 addition & 1 deletion agent-executor/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.bsc.langgraph4j</groupId>
<artifactId>langgraph4j-parent</artifactId>
<version>1.2.4</version>
<version>1.2.5</version>
<relativePath>..</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.bsc.langgraph4j</groupId>
<artifactId>langgraph4j-parent</artifactId>
<version>1.2.4</version>
<version>1.2.5</version>
</parent>

<artifactId>langgraph4j-core</artifactId>
Expand Down
115 changes: 106 additions & 9 deletions core/src/main/java/org/bsc/langgraph4j/state/AppenderChannel.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;


/**
Expand All @@ -20,6 +19,24 @@
@Slf4j
public class AppenderChannel<T> implements Channel<List<T>> {

/**
* A functional interface that is used to remove elements from a list.
*
* @param <T> the type of elements in the list
*/
@FunctionalInterface
public interface RemoveIdentifier<T> {
/**
* Compares the specified element with the element at the given index.
*
* @param <T> 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 );
}

private final Reducer<List<T>> reducer;
private final Supplier<List<T>> defaultProvider;

Expand Down Expand Up @@ -58,16 +75,14 @@ public static <T> AppenderChannel<T> of( Supplier<List<T>> defaultProvider ) {
/**
* Constructs a new instance of {@code AppenderChannel} with the specified default provider.
*
* @param <T> 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<List<T>> defaultProvider) {
this.reducer = new Reducer<List<T>>() {
private AppenderChannel( Supplier<List<T>> 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 <T> 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
Expand All @@ -84,6 +99,77 @@ public List<T> apply(List<T> left, List<T> 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 <T> 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<T> remove(List<T> list, RemoveIdentifier<T> removeIdentifier ) {
var result = new ArrayList<>(list);
removeFromList(result, removeIdentifier);
return unmodifiableList(result);
}

/**
* Removes an element from the list that matches the specified identifier.
*
* <p>This method iterates over the provided list and removes the first element for which the
* {@link RemoveIdentifier#compareTo} method returns zero.</p>
*
* @param result the list to be modified
* @param removeIdentifier the identifier used to find the element to remove
*/
private void removeFromList(List<T> result, RemoveIdentifier<T> removeIdentifier ) {
for( int i = 0; i < result.size(); i++ ) {
if( removeIdentifier.compareTo( result.get(i), i ) == 0 ) {
result.remove(i);
break;
}
}
}

/**
* Represents a record for data removal operations with generic types.
*
* @param <T> the type of elements in the old values list
*/
record RemoveData<T>( List<T> 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);
}
};

/**
* 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<T>} object with updated old and new values after removing identifiers
*/
@SuppressWarnings("unchecked")
private RemoveData<T> evaluateRemoval(List<T> 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<T>) value;
removeFromList( result.oldValues(), removeIdentifier );

});
return result;

}

/**
* Updates the value for a given key in the channel.
*
Expand All @@ -95,28 +181,39 @@ public List<T> apply(List<T> left, List<T> 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<T>)oldValue, (RemoveIdentifier<T>)newValue);
}
List<?> list = null;
if (newValue instanceof List) {
list = (List<?>) newValue;
list = (List<Object>) 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<T>)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;
Expand Down
34 changes: 34 additions & 0 deletions core/src/main/java/org/bsc/langgraph4j/state/RemoveByHash.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.bsc.langgraph4j.state;

import java.util.Objects;

/**
* Represents a record that implements the {@link AppenderChannel.RemoveIdentifier<T>} interface.
*
* @param <T> the type of the value to be associated with this RemoveByHash instance
*/
public record RemoveByHash<T>(T value ) implements AppenderChannel.RemoveIdentifier<T> {
/**
* Compares the hash code of this object with another element at a specific index.
*
* @param <T> 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 <T> the type of the value
* @param value the value to store in the {@code RemoveByHash}
* @return a new {@code RemoveByHash} instance
*/
public static <T> RemoveByHash<T> of ( T value ) {
return new RemoveByHash<>(value);
}
}
Loading

0 comments on commit 9c53762

Please sign in to comment.