// Path: conversation-kit/src/main/java/com/conversationkit/model/IConversationEdge.java
// public interface IConversationEdge<I extends IConversationIntent, S extends IConversationState> {
// 
//     public Integer getEndNodeId();
//     
//     public String getIntentId();
//     
//     /**
//      * Additional logic to perform before continuing the conversation along this edge. It's possible for
//      * a node to have multiple edges with the same intentId. Each edge could have different preconditions
//      * that must be met in order to match. The validate function allows the engine to check the preconditions
//      * against the current state and return true or false. If the edge intent matches but the validate
//      * fails, the engine will look at the next edge with the same intent.
//      * @param intent the intent from the current user input
//      * @param state the current conversation sate
//      * @return true if the edge should validate, false otherwise
//      */
//     public boolean validate(I intent, S state);
//     
//     /**
//      * Side effects that should occur if this edge is validated. Side effects are
//      * ways to change the state before the next step in the conversation. The type
//      * of Object returned depends on the {@link Redux} {@link Middleware}s that are installed.
//      * <p>
//      * Side effects should be used to perform actions like loading additional data
//      * from a web service or database that is required to respond to the user. The state
//      * that is passed in should not be modified directly. Instead the list of actions
//      * returned will be passed sequentially to the Redux middleware chain and executed in order.
//      * @param intent the validated intent
//      * @param state immutable copy of the state
//      * @return a list of actions to perform before the next conversation step
//      */
//     public List<Object> getSideEffects(I intent, S state);
// }
// 
// Path: nlu-core/src/main/java/com/conversationkit/model/IConversationIntent.java
// public interface IConversationIntent {
//     /**
//      * @return unique ID for the intent.
//      */
//     public String getIntentId();
//     /**
//      * Slots are parameters collected from the user's input while detecting intent. As an example, a
//      * a slot might be a color. So in the user input, "Press the red button" the intent
//      * might be <code>PRESS_BUTTON</code> with a slot of <code>color</code> set to <code>red</code>.
//      * @return map of slot keys to slot values
//      */
//     public Map<String,Object> getSlots();
//     
//     public boolean getAllRequiredSlotsFilled();
// }
// 
// Path: conversation-kit/src/main/java/com/conversationkit/model/IConversationState.java
// public interface IConversationState <S extends IConversationState> extends Function<Map,S> {
//     /**
//      * @return the id of the node representing the last message sent to the user
//      */
//     public Integer getCurrentNodeId();
//     /**
//      * @return the number of consecutive times the engine has misunderstood the user's input.
//      */
//     public Integer getMisunderstoodCount();
//     /**
//      * @return a unique string representation of the current user
//      */
//     public String getUserId();
//     /**
//      * @return the current state as a Map
//      */
//     public Map getStateAsMap();
// }


import com.conversationkit.model.IConversationEdge;
import com.conversationkit.model.IConversationIntent;
import com.conversationkit.model.IConversationState;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiFunction;
            this.sideEffects.add(effect);
        }
    }
    
    public ConversationEdge(Integer endNodeId, String intentId, BiFunction<I, S, Object> sideEffect) {
        this.endNodeId = endNodeId;
        this.id = intentId;
        this.validateFunction = (intent, state) -> {
            return true;
        };
        this.sideEffects = Arrays.asList(sideEffect);
    }

    @Override
    public Integer getEndNodeId() {
        return endNodeId;
    }

    @Override
    public String getIntentId() {
        return id;
    }

    @Override
    public String toString() {
        return "ConversationEdge {" + getIntentId() + '}';
    }

    @Override
    public boolean validate(I intent, S state) {
// Path: conversation-kit/src/main/java/com/conversationkit/model/IConversationEdge.java
// public interface IConversationEdge<I extends IConversationIntent, S extends IConversationState> {
// 
//     public Integer getEndNodeId();
//     
//     public String getIntentId();
//     
//     /**
//      * Additional logic to perform before continuing the conversation along this edge. It's possible for
//      * a node to have multiple edges with the same intentId. Each edge could have different preconditions
//      * that must be met in order to match. The validate function allows the engine to check the preconditions
//      * against the current state and return true or false. If the edge intent matches but the validate
//      * fails, the engine will look at the next edge with the same intent.
//      * @param intent the intent from the current user input
//      * @param state the current conversation sate
//      * @return true if the edge should validate, false otherwise
//      */
//     public boolean validate(I intent, S state);
//     
//     /**
//      * Side effects that should occur if this edge is validated. Side effects are
//      * ways to change the state before the next step in the conversation. The type
//      * of Object returned depends on the {@link Redux} {@link Middleware}s that are installed.
//      * <p>
//      * Side effects should be used to perform actions like loading additional data
//      * from a web service or database that is required to respond to the user. The state
//      * that is passed in should not be modified directly. Instead the list of actions
//      * returned will be passed sequentially to the Redux middleware chain and executed in order.
//      * @param intent the validated intent
//      * @param state immutable copy of the state
//      * @return a list of actions to perform before the next conversation step
//      */
//     public List<Object> getSideEffects(I intent, S state);
// }
// 
// Path: nlu-core/src/main/java/com/conversationkit/model/IConversationIntent.java
// public interface IConversationIntent {
//     /**
//      * @return unique ID for the intent.
//      */
//     public String getIntentId();
//     /**
//      * Slots are parameters collected from the user's input while detecting intent. As an example, a
//      * a slot might be a color. So in the user input, "Press the red button" the intent
//      * might be <code>PRESS_BUTTON</code> with a slot of <code>color</code> set to <code>red</code>.
//      * @return map of slot keys to slot values
//      */
//     public Map<String,Object> getSlots();
//     
//     public boolean getAllRequiredSlotsFilled();
// }
// 
// Path: conversation-kit/src/main/java/com/conversationkit/model/IConversationState.java
// public interface IConversationState <S extends IConversationState> extends Function<Map,S> {
//     /**
//      * @return the id of the node representing the last message sent to the user
//      */
//     public Integer getCurrentNodeId();
//     /**
//      * @return the number of consecutive times the engine has misunderstood the user's input.
//      */
//     public Integer getMisunderstoodCount();
//     /**
//      * @return a unique string representation of the current user
//      */
//     public String getUserId();
//     /**
//      * @return the current state as a Map
//      */
//     public Map getStateAsMap();
// }


// Path: conversation-kit/src/main/java/com/conversationkit/impl/edge/ConversationEdge.java
import com.conversationkit.model.IConversationEdge;
import com.conversationkit.model.IConversationIntent;
import com.conversationkit.model.IConversationState;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiFunction;

            this.sideEffects.add(effect);
        }
    }
    
    public ConversationEdge(Integer endNodeId, String intentId, BiFunction<I, S, Object> sideEffect) {
        this.endNodeId = endNodeId;
        this.id = intentId;
        this.validateFunction = (intent, state) -> {
            return true;
        };
        this.sideEffects = Arrays.asList(sideEffect);
    }

    @Override
    public Integer getEndNodeId() {
        return endNodeId;
    }

    @Override
    public String getIntentId() {
        return id;
    }

    @Override
    public String toString() {
        return "ConversationEdge {" + getIntentId() + '}';
    }

    @Override
    public boolean validate(I intent, S state) {
        return validateFunction.apply(intent, state);