Graph State is the core data structure of the graph execution engine in the Blades framework, used to pass and share data between various nodes in the graph. It is a key-value mapping structure used to store and pass data during graph execution, allows information sharing between nodes, and supports state merging and cloning operations.
Graph state is used to store data that needs to be passed during graph execution. The key is of string type, and the value can be of any type, supporting flexible data storage.
type State map[string]anyfunc (s State) Clone() State { return State(maps.Clone(map[string]any(s)))}maps.Clone, nested references are sharedmergeStates(base State, updates ...State) StateThe graph state in Blades has the following characteristics:
initialState := graph.State{ "key1": "value1", "key2": 123, "key3": []string{"a", "b", "c"},}handler := func(ctx context.Context, state graph.State) (graph.State, error) { // loading state value1 := state["key1"].(string)
// updating state newState := state.Clone() newState["result"] = "processed: " + value1
return newState, nil}// create graph and add nodesg := graph.NewGraph()g.AddNode("node1", handler1)g.AddNode("node2", handler2)g.AddEdge("node1", "node2")g.SetEntryPoint("node1")g.SetFinishPoint("node2")
// compile graph and executeexecutor, err := g.Compile()if err != nil { log.Fatal(err)}
result, err := executor.Execute(context.Background(), initialState)// clone state to avoid modifying the original statenewState := state.Clone()newState["newKey"] = "newValue"// merge multiple statesmergedState := mergeStates(state1, state2, state3)// safely access state valueif value, ok := state["key"]; ok { // process value processValue(value)}// define node handler functionsfunc processDataNode(ctx context.Context, state graph.State) (graph.State, error) { // read input data inputData, ok := state["input"].(string) if !ok { return nil, fmt.Errorf("missing input data") }
// process data : here we convert the input data to uppercase(eg: a-> A) processedData := strings.ToUpper(inputData)
// create new state newState := state.Clone() newState["processed"] = processedData newState["timestamp"] = time.Now()
return newState, nil}
func validateDataNode(ctx context.Context, state graph.State) (graph.State, error) { // read processed data processedData, ok := state["processed"].(string) if !ok { return nil, fmt.Errorf("missing processed data") }
// validate data isValid := len(processedData) > 0
// update state newState := state.Clone() newState["valid"] = isValid newState["validation_time"] = time.Now()
return newState, nil}
// create graphg := graph.NewGraph()g.AddNode("process", processDataNode)g.AddNode("validate", validateDataNode)g.AddEdge("process", "validate")g.SetEntryPoint("process")g.SetFinishPoint("validate")
// compile graphexecutor, err := g.Compile()if err != nil { log.Fatal(err)}
// create initial stateinitialState := graph.State{ "input": "hello world", "source": "user_input",}
// execute graphresult, err := executor.Execute(context.Background(), initialState)if err != nil { log.Fatal(err)}
// output resultfmt.Printf("Final state: %+v\n", result)