wesichain-graph 0.3.0

Rust-native LLM agents & chains with resumable ReAct workflows
Documentation
use serde::{Deserialize, Serialize};
use tempfile::tempdir;
use wesichain_graph::{
    Checkpoint, Checkpointer, FileCheckpointer, GraphState, HistoryCheckpointer, StateSchema,
};

#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq)]
struct DemoState {
    count: i32,
}

impl StateSchema for DemoState {
    type Update = Self;
    fn apply(_: &Self, update: Self) -> Self {
        update
    }
}

#[tokio::test]
async fn file_checkpointer_appends_and_loads_latest() {
    let dir = tempdir().unwrap();
    let checkpointer = FileCheckpointer::new(dir.path());

    let mut state = GraphState::new(DemoState { count: 1 });
    let first = Checkpoint::new(
        "thread-1".to_string(),
        state.clone(),
        1,
        "node-1".to_string(),
        vec![],
    );
    checkpointer.save(&first).await.unwrap();

    state.data.count = 2;
    let second = Checkpoint::new(
        "thread-1".to_string(),
        state.clone(),
        2,
        "node-2".to_string(),
        vec![],
    );

    checkpointer.save(&second).await.unwrap();

    let loaded: Checkpoint<DemoState> = checkpointer.load("thread-1").await.unwrap().unwrap();
    assert_eq!(loaded.state.data.count, 2);

    let path = dir.path().join("thread-1.jsonl");
    assert!(path.exists());
}

#[tokio::test]
async fn file_checkpointer_lists_metadata() {
    let dir = tempdir().unwrap();
    let checkpointer = FileCheckpointer::new(dir.path());

    let first = Checkpoint::new(
        "thread-2".to_string(),
        GraphState::new(DemoState { count: 1 }),
        1,
        "inc".to_string(),
        vec![],
    );
    let second = Checkpoint::new(
        "thread-2".to_string(),
        GraphState::new(DemoState { count: 2 }),
        2,
        "inc".to_string(),
        vec![],
    );

    checkpointer.save(&first).await.unwrap();
    checkpointer.save(&second).await.unwrap();

    let history = <FileCheckpointer as HistoryCheckpointer<DemoState>>::list_checkpoints(
        &checkpointer,
        "thread-2",
    )
    .await
    .unwrap();
    assert_eq!(history.len(), 2);
    assert_eq!(history[0].seq, 1);
    assert_eq!(history[1].seq, 2);
    assert!(!history[0].created_at.is_empty());
}