|
1 | | -from typing import List, Dict, Any |
| 1 | +from typing import List, Dict, Any, Optional |
2 | 2 | from graphai.nodes.base import _Node |
3 | 3 | from graphai.callback import Callback |
4 | 4 | from semantic_router.utils.logger import logger |
5 | 5 |
|
6 | 6 |
|
7 | 7 | class Graph: |
8 | | - def __init__(self, max_steps: int = 10): |
9 | | - self.nodes = {} |
10 | | - self.edges = [] |
11 | | - self.start_node = None |
12 | | - self.end_nodes = [] |
| 8 | + def __init__(self, max_steps: int = 10, initial_state: Optional[Dict[str, Any]] = None): |
| 9 | + self.nodes: Dict[str, _Node] = {} |
| 10 | + self.edges: List[Any] = [] |
| 11 | + self.start_node: Optional[_Node] = None |
| 12 | + self.end_nodes: List[_Node] = [] |
13 | 13 | self.Callback = Callback |
14 | 14 | self.callback = None |
15 | 15 | self.max_steps = max_steps |
| 16 | + self.state = initial_state or {} |
| 17 | + |
| 18 | + # Allow getting and setting the graph's internal state |
| 19 | + def get_state(self) -> Dict[str, Any]: |
| 20 | + """Get the current graph state.""" |
| 21 | + return self.state |
| 22 | + |
| 23 | + def set_state(self, state: Dict[str, Any]): |
| 24 | + """Set the graph state.""" |
| 25 | + self.state = state |
| 26 | + |
| 27 | + def update_state(self, values: Dict[str, Any]): |
| 28 | + """Update the graph state with new values.""" |
| 29 | + self.state.update(values) |
| 30 | + |
| 31 | + def reset_state(self): |
| 32 | + """Reset the graph state to an empty dict.""" |
| 33 | + self.state = {} |
16 | 34 |
|
17 | 35 | def add_node(self, node): |
18 | 36 | if node.name in self.nodes: |
@@ -100,17 +118,20 @@ async def execute(self, input): |
100 | 118 | self.callback = self.get_callback() |
101 | 119 | current_node = self.start_node |
102 | 120 | state = input |
| 121 | + # Don't reset the graph state if it was initialized with initial_state |
103 | 122 | steps = 0 |
104 | 123 | while True: |
105 | 124 | # we invoke the node here |
106 | 125 | if current_node.stream: |
107 | 126 | # add callback tokens and param here if we are streaming |
108 | 127 | await self.callback.start_node(node_name=current_node.name) |
109 | | - output = await current_node.invoke(input=state, callback=self.callback) |
| 128 | + # Include graph's internal state in the node execution context |
| 129 | + output = await current_node.invoke(input=state, callback=self.callback, state=self.state) |
110 | 130 | self._validate_output(output=output, node_name=current_node.name) |
111 | 131 | await self.callback.end_node(node_name=current_node.name) |
112 | 132 | else: |
113 | | - output = await current_node.invoke(input=state) |
| 133 | + # Include graph's internal state in the node execution context |
| 134 | + output = await current_node.invoke(input=state, state=self.state) |
114 | 135 | self._validate_output(output=output, node_name=current_node.name) |
115 | 136 | # add output to state |
116 | 137 | state = {**state, **output} |
@@ -142,10 +163,21 @@ def get_callback(self): |
142 | 163 | return self.callback |
143 | 164 |
|
144 | 165 | def _get_node_by_name(self, node_name: str) -> _Node: |
145 | | - for node in self.nodes: |
146 | | - if node.name == node_name: |
147 | | - return node |
148 | | - raise Exception(f"Node with name {node_name} not found.") |
| 166 | + """Get a node by its name. |
| 167 | + |
| 168 | + Args: |
| 169 | + node_name: The name of the node to find. |
| 170 | + |
| 171 | + Returns: |
| 172 | + The node with the given name. |
| 173 | + |
| 174 | + Raises: |
| 175 | + Exception: If no node with the given name is found. |
| 176 | + """ |
| 177 | + node = self.nodes.get(node_name) |
| 178 | + if node is None: |
| 179 | + raise Exception(f"Node with name {node_name} not found.") |
| 180 | + return node |
149 | 181 |
|
150 | 182 | def _get_next_node(self, current_node): |
151 | 183 | for edge in self.edges: |
|
0 commit comments