use langchainrust::{
GraphBuilder, START, END,
AgentState, StateUpdate,
};
use langchainrust::langgraph::SubgraphNode;
use std::time::Duration;
#[tokio::test]
async fn test_basic_subgraph() {
let subgraph = GraphBuilder::<AgentState>::new()
.add_node_fn("sub_process", |state| {
let mut s = state.clone();
s.set_output("subgraph_done".to_string());
Ok(StateUpdate::full(s))
})
.add_edge(START, "sub_process")
.add_edge("sub_process", END)
.compile()
.unwrap();
let parent = GraphBuilder::<AgentState>::new()
.add_subgraph_same_state("subworkflow", subgraph)
.add_edge(START, "subworkflow")
.add_edge("subworkflow", END)
.compile()
.unwrap();
let input = AgentState::new("test".to_string());
let result = parent.invoke(input).await.unwrap();
assert!(result.final_state.output.is_some());
assert_eq!(result.final_state.output.unwrap(), "subgraph_done");
}
#[tokio::test]
async fn test_nested_subgraphs() {
let inner = GraphBuilder::<AgentState>::new()
.add_node_fn("inner_node", |state| {
let mut s = state.clone();
s.input = format!("inner:{}", s.input);
Ok(StateUpdate::full(s))
})
.add_edge(START, "inner_node")
.add_edge("inner_node", END)
.compile()
.unwrap();
let middle = GraphBuilder::<AgentState>::new()
.add_subgraph_same_state("inner_workflow", inner)
.add_node_fn("middle_node", |state| {
let mut s = state.clone();
s.input = format!("middle:{}", s.input);
Ok(StateUpdate::full(s))
})
.add_edge(START, "inner_workflow")
.add_edge("inner_workflow", "middle_node")
.add_edge("middle_node", END)
.compile()
.unwrap();
let outer = GraphBuilder::<AgentState>::new()
.add_node_fn("outer_node", |state| {
let mut s = state.clone();
s.input = format!("outer:{}", s.input);
Ok(StateUpdate::full(s))
})
.add_subgraph_same_state("middle_workflow", middle)
.add_edge(START, "outer_node")
.add_edge("outer_node", "middle_workflow")
.add_edge("middle_workflow", END)
.compile()
.unwrap();
let input = AgentState::new("test".to_string());
let result = outer.invoke(input).await.unwrap();
assert!(result.final_state.input.contains("outer"));
assert!(result.final_state.input.contains("middle"));
assert!(result.final_state.input.contains("inner"));
}
#[tokio::test]
async fn test_subgraph_with_multiple_nodes() {
let subgraph = GraphBuilder::<AgentState>::new()
.add_node_fn("step1", |state| {
let mut s = state.clone();
s.add_message(langchainrust::MessageEntry::ai("step1_done".to_string()));
Ok(StateUpdate::full(s))
})
.add_node_fn("step2", |state| {
let mut s = state.clone();
s.add_message(langchainrust::MessageEntry::ai("step2_done".to_string()));
Ok(StateUpdate::full(s))
})
.add_node_fn("step3", |state| {
let mut s = state.clone();
s.add_message(langchainrust::MessageEntry::ai("step3_done".to_string()));
s.set_output("all_steps_done".to_string());
Ok(StateUpdate::full(s))
})
.add_edge(START, "step1")
.add_edge("step1", "step2")
.add_edge("step2", "step3")
.add_edge("step3", END)
.compile()
.unwrap();
let parent = GraphBuilder::<AgentState>::new()
.add_node_fn("before", |state| {
let mut s = state.clone();
s.add_message(langchainrust::MessageEntry::ai("before_subgraph".to_string()));
Ok(StateUpdate::full(s))
})
.add_subgraph_same_state("workflow", subgraph)
.add_node_fn("after", |state| {
let mut s = state.clone();
s.add_message(langchainrust::MessageEntry::ai("after_subgraph".to_string()));
Ok(StateUpdate::full(s))
})
.add_edge(START, "before")
.add_edge("before", "workflow")
.add_edge("workflow", "after")
.add_edge("after", END)
.compile()
.unwrap();
let input = AgentState::new("test".to_string());
let result = parent.invoke(input).await.unwrap();
assert!(result.final_state.output.is_some());
assert_eq!(result.final_state.output.unwrap(), "all_steps_done");
assert!(result.final_state.messages.len() >= 5);
}
#[tokio::test]
async fn test_async_subgraph() {
let subgraph = GraphBuilder::<AgentState>::new()
.add_async_node("async_process", |state: &AgentState| {
let state = state.clone();
async move {
tokio::time::sleep(Duration::from_millis(10)).await;
let mut s = state;
s.set_output("async_done".to_string());
Ok(StateUpdate::full(s))
}
})
.add_edge(START, "async_process")
.add_edge("async_process", END)
.compile()
.unwrap();
let parent = GraphBuilder::<AgentState>::new()
.add_subgraph_same_state("async_workflow", subgraph)
.add_edge(START, "async_workflow")
.add_edge("async_workflow", END)
.compile()
.unwrap();
let input = AgentState::new("test".to_string());
let result = parent.invoke(input).await.unwrap();
assert!(result.final_state.output.is_some());
assert_eq!(result.final_state.output.unwrap(), "async_done");
}