graphai-lib 0.0.2__tar.gz → 0.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- graphai_lib-0.0.4/PKG-INFO +34 -0
- {graphai_lib-0.0.2 → graphai_lib-0.0.4}/README.md +7 -1
- {graphai_lib-0.0.2 → graphai_lib-0.0.4}/graphai/graph.py +84 -22
- {graphai_lib-0.0.2 → graphai_lib-0.0.4}/graphai/nodes/base.py +11 -5
- graphai_lib-0.0.4/graphai_lib.egg-info/PKG-INFO +34 -0
- graphai_lib-0.0.4/graphai_lib.egg-info/SOURCES.txt +13 -0
- graphai_lib-0.0.4/graphai_lib.egg-info/dependency_links.txt +1 -0
- graphai_lib-0.0.4/graphai_lib.egg-info/requires.txt +19 -0
- graphai_lib-0.0.4/graphai_lib.egg-info/top_level.txt +1 -0
- graphai_lib-0.0.4/pyproject.toml +32 -0
- graphai_lib-0.0.4/setup.cfg +4 -0
- graphai_lib-0.0.2/PKG-INFO +0 -25
- graphai_lib-0.0.2/pyproject.toml +0 -29
- {graphai_lib-0.0.2 → graphai_lib-0.0.4}/graphai/__init__.py +0 -0
- {graphai_lib-0.0.2 → graphai_lib-0.0.4}/graphai/callback.py +0 -0
- {graphai_lib-0.0.2 → graphai_lib-0.0.4}/graphai/nodes/__init__.py +0 -0
- {graphai_lib-0.0.2 → graphai_lib-0.0.4}/graphai/utils.py +0 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: graphai-lib
|
3
|
+
Version: 0.0.4
|
4
|
+
Summary: Not an AI framework
|
5
|
+
Requires-Python: <3.14,>=3.10
|
6
|
+
Description-Content-Type: text/markdown
|
7
|
+
Requires-Dist: semantic-router>=0.1.5
|
8
|
+
Requires-Dist: networkx>=3.4.2
|
9
|
+
Requires-Dist: matplotlib>=3.10.0
|
10
|
+
Provides-Extra: dev
|
11
|
+
Requires-Dist: ipykernel>=6.25.0; extra == "dev"
|
12
|
+
Requires-Dist: ruff>=0.1.5; extra == "dev"
|
13
|
+
Requires-Dist: pytest>=8.2.0; extra == "dev"
|
14
|
+
Requires-Dist: pytest-mock>=3.12.0; extra == "dev"
|
15
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
|
16
|
+
Requires-Dist: pytest-xdist>=3.5.0; extra == "dev"
|
17
|
+
Requires-Dist: pytest-asyncio>=0.24.0; extra == "dev"
|
18
|
+
Requires-Dist: mypy>=1.7.1; extra == "dev"
|
19
|
+
Requires-Dist: black[jupyter]<24.5.0,>=23.12.1; extra == "dev"
|
20
|
+
Provides-Extra: docs
|
21
|
+
Requires-Dist: pydoc-markdown>=4.8.2; python_version < "3.12" and extra == "docs"
|
22
|
+
|
23
|
+
# Philosophy
|
24
|
+
|
25
|
+
1. Async-first
|
26
|
+
2. Minimize abstractions
|
27
|
+
3. One way to do one thing
|
28
|
+
4. Graph-based AI
|
29
|
+
|
30
|
+
## Installation
|
31
|
+
|
32
|
+
```
|
33
|
+
pip install -qU graphai-lib
|
34
|
+
```
|
@@ -1,21 +1,41 @@
|
|
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
|
-
self.nodes
|
36
|
+
if node.name in self.nodes:
|
37
|
+
raise Exception(f"Node with name '{node.name}' already exists.")
|
38
|
+
self.nodes[node.name] = node
|
19
39
|
if node.is_start:
|
20
40
|
if self.start_node is not None:
|
21
41
|
raise Exception(
|
@@ -27,10 +47,37 @@ class Graph:
|
|
27
47
|
if node.is_end:
|
28
48
|
self.end_nodes.append(node)
|
29
49
|
|
30
|
-
def add_edge(self, source: _Node, destination: _Node):
|
31
|
-
|
32
|
-
|
33
|
-
|
50
|
+
def add_edge(self, source: _Node | str, destination: _Node | str):
|
51
|
+
"""Adds an edge between two nodes that already exist in the graph.
|
52
|
+
|
53
|
+
Args:
|
54
|
+
source: The source node or its name.
|
55
|
+
destination: The destination node or its name.
|
56
|
+
"""
|
57
|
+
source_node, destination_node = None, None
|
58
|
+
# get source node from graph
|
59
|
+
if isinstance(source, str):
|
60
|
+
source_node = self.nodes.get(source)
|
61
|
+
else:
|
62
|
+
# Check if it's a node-like object by looking for required attributes
|
63
|
+
if hasattr(source, 'name'):
|
64
|
+
source_node = self.nodes.get(source.name)
|
65
|
+
if source_node is None:
|
66
|
+
raise ValueError(
|
67
|
+
f"Node with name '{source.name if hasattr(source, 'name') else source}' not found."
|
68
|
+
)
|
69
|
+
# get destination node from graph
|
70
|
+
if isinstance(destination, str):
|
71
|
+
destination_node = self.nodes.get(destination)
|
72
|
+
else:
|
73
|
+
# Check if it's a node-like object by looking for required attributes
|
74
|
+
if hasattr(destination, 'name'):
|
75
|
+
destination_node = self.nodes.get(destination.name)
|
76
|
+
if destination_node is None:
|
77
|
+
raise ValueError(
|
78
|
+
f"Node with name '{destination.name if hasattr(destination, 'name') else destination}' not found."
|
79
|
+
)
|
80
|
+
edge = Edge(source_node, destination_node)
|
34
81
|
self.edges.append(edge)
|
35
82
|
|
36
83
|
def add_router(self, sources: list[_Node], router: _Node, destinations: List[_Node]):
|
@@ -71,17 +118,20 @@ class Graph:
|
|
71
118
|
self.callback = self.get_callback()
|
72
119
|
current_node = self.start_node
|
73
120
|
state = input
|
121
|
+
# Don't reset the graph state if it was initialized with initial_state
|
74
122
|
steps = 0
|
75
123
|
while True:
|
76
124
|
# we invoke the node here
|
77
125
|
if current_node.stream:
|
78
126
|
# add callback tokens and param here if we are streaming
|
79
127
|
await self.callback.start_node(node_name=current_node.name)
|
80
|
-
|
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)
|
81
130
|
self._validate_output(output=output, node_name=current_node.name)
|
82
131
|
await self.callback.end_node(node_name=current_node.name)
|
83
132
|
else:
|
84
|
-
|
133
|
+
# Include graph's internal state in the node execution context
|
134
|
+
output = await current_node.invoke(input=state, state=self.state)
|
85
135
|
self._validate_output(output=output, node_name=current_node.name)
|
86
136
|
# add output to state
|
87
137
|
state = {**state, **output}
|
@@ -113,10 +163,21 @@ class Graph:
|
|
113
163
|
return self.callback
|
114
164
|
|
115
165
|
def _get_node_by_name(self, node_name: str) -> _Node:
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
120
181
|
|
121
182
|
def _get_next_node(self, current_node):
|
122
183
|
for edge in self.edges:
|
@@ -139,7 +200,7 @@ class Graph:
|
|
139
200
|
|
140
201
|
G = nx.DiGraph()
|
141
202
|
|
142
|
-
for node in self.nodes:
|
203
|
+
for node in self.nodes.values():
|
143
204
|
G.add_node(node.name)
|
144
205
|
|
145
206
|
for edge in self.edges:
|
@@ -173,10 +234,11 @@ class Graph:
|
|
173
234
|
pos[node] = (pos[node][0] - x_center, pos[node][1])
|
174
235
|
|
175
236
|
# Scale the layout
|
176
|
-
max_x = max(abs(p[0]) for p in pos.values())
|
177
|
-
max_y = max(abs(p[1]) for p in pos.values())
|
178
|
-
|
179
|
-
|
237
|
+
max_x = max(abs(p[0]) for p in pos.values()) if pos else 1
|
238
|
+
max_y = max(abs(p[1]) for p in pos.values()) if pos else 1
|
239
|
+
if max_x > 0 and max_y > 0:
|
240
|
+
scale = min(0.8 / max_x, 0.8 / max_y)
|
241
|
+
pos = {node: (x * scale, y * scale) for node, (x, y) in pos.items()}
|
180
242
|
|
181
243
|
else:
|
182
244
|
print("Warning: The graph contains cycles. Visualization will use a spring layout.")
|
@@ -31,6 +31,7 @@ class _Node:
|
|
31
31
|
start: bool = False,
|
32
32
|
end: bool = False,
|
33
33
|
stream: bool = False,
|
34
|
+
name: str | None = None,
|
34
35
|
) -> Callable:
|
35
36
|
"""Decorator validating node structure.
|
36
37
|
"""
|
@@ -104,7 +105,7 @@ class _Node:
|
|
104
105
|
return "\n".join(signature_components)
|
105
106
|
|
106
107
|
@classmethod
|
107
|
-
async def invoke(cls, input: Dict[str, Any], callback: Optional[Callback] = None):
|
108
|
+
async def invoke(cls, input: Dict[str, Any], callback: Optional[Callback] = None, state: Optional[Dict[str, Any]] = None):
|
108
109
|
if callback:
|
109
110
|
if stream:
|
110
111
|
input["callback"] = callback
|
@@ -112,12 +113,16 @@ class _Node:
|
|
112
113
|
raise ValueError(
|
113
114
|
f"Error in node {func.__name__}. When callback provided, stream must be True."
|
114
115
|
)
|
116
|
+
# Add state to the input if present and the parameter exists in the function signature
|
117
|
+
if state is not None and "state" in cls._func_signature.parameters:
|
118
|
+
input["state"] = state
|
119
|
+
|
115
120
|
instance = cls()
|
116
121
|
out = await instance.execute(**input)
|
117
122
|
return out
|
118
123
|
|
119
124
|
NodeClass.__name__ = func.__name__
|
120
|
-
NodeClass.name = func.__name__
|
125
|
+
NodeClass.name = name or func.__name__
|
121
126
|
NodeClass.__doc__ = func.__doc__
|
122
127
|
NodeClass.is_start = start
|
123
128
|
NodeClass.is_end = end
|
@@ -132,14 +137,15 @@ class _Node:
|
|
132
137
|
start: bool = False,
|
133
138
|
end: bool = False,
|
134
139
|
stream: bool = False,
|
140
|
+
name: str | None = None,
|
135
141
|
):
|
136
142
|
# We must wrap the call to the decorator in a function for it to work
|
137
143
|
# correctly with or without parenthesis
|
138
|
-
def wrap(func: Callable, start=start, end=end, stream=stream) -> Callable:
|
139
|
-
return self._node(func=func, start=start, end=end, stream=stream)
|
144
|
+
def wrap(func: Callable, start=start, end=end, stream=stream, name=name) -> Callable:
|
145
|
+
return self._node(func=func, start=start, end=end, stream=stream, name=name)
|
140
146
|
if func:
|
141
147
|
# Decorator is called without parenthesis
|
142
|
-
return wrap(func=func, start=start, end=end, stream=stream)
|
148
|
+
return wrap(func=func, start=start, end=end, stream=stream, name=name)
|
143
149
|
# Decorator is called with parenthesis
|
144
150
|
return wrap
|
145
151
|
|
@@ -0,0 +1,34 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: graphai-lib
|
3
|
+
Version: 0.0.4
|
4
|
+
Summary: Not an AI framework
|
5
|
+
Requires-Python: <3.14,>=3.10
|
6
|
+
Description-Content-Type: text/markdown
|
7
|
+
Requires-Dist: semantic-router>=0.1.5
|
8
|
+
Requires-Dist: networkx>=3.4.2
|
9
|
+
Requires-Dist: matplotlib>=3.10.0
|
10
|
+
Provides-Extra: dev
|
11
|
+
Requires-Dist: ipykernel>=6.25.0; extra == "dev"
|
12
|
+
Requires-Dist: ruff>=0.1.5; extra == "dev"
|
13
|
+
Requires-Dist: pytest>=8.2.0; extra == "dev"
|
14
|
+
Requires-Dist: pytest-mock>=3.12.0; extra == "dev"
|
15
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
|
16
|
+
Requires-Dist: pytest-xdist>=3.5.0; extra == "dev"
|
17
|
+
Requires-Dist: pytest-asyncio>=0.24.0; extra == "dev"
|
18
|
+
Requires-Dist: mypy>=1.7.1; extra == "dev"
|
19
|
+
Requires-Dist: black[jupyter]<24.5.0,>=23.12.1; extra == "dev"
|
20
|
+
Provides-Extra: docs
|
21
|
+
Requires-Dist: pydoc-markdown>=4.8.2; python_version < "3.12" and extra == "docs"
|
22
|
+
|
23
|
+
# Philosophy
|
24
|
+
|
25
|
+
1. Async-first
|
26
|
+
2. Minimize abstractions
|
27
|
+
3. One way to do one thing
|
28
|
+
4. Graph-based AI
|
29
|
+
|
30
|
+
## Installation
|
31
|
+
|
32
|
+
```
|
33
|
+
pip install -qU graphai-lib
|
34
|
+
```
|
@@ -0,0 +1,13 @@
|
|
1
|
+
README.md
|
2
|
+
pyproject.toml
|
3
|
+
graphai/__init__.py
|
4
|
+
graphai/callback.py
|
5
|
+
graphai/graph.py
|
6
|
+
graphai/utils.py
|
7
|
+
graphai/nodes/__init__.py
|
8
|
+
graphai/nodes/base.py
|
9
|
+
graphai_lib.egg-info/PKG-INFO
|
10
|
+
graphai_lib.egg-info/SOURCES.txt
|
11
|
+
graphai_lib.egg-info/dependency_links.txt
|
12
|
+
graphai_lib.egg-info/requires.txt
|
13
|
+
graphai_lib.egg-info/top_level.txt
|
@@ -0,0 +1 @@
|
|
1
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
semantic-router>=0.1.5
|
2
|
+
networkx>=3.4.2
|
3
|
+
matplotlib>=3.10.0
|
4
|
+
|
5
|
+
[dev]
|
6
|
+
ipykernel>=6.25.0
|
7
|
+
ruff>=0.1.5
|
8
|
+
pytest>=8.2.0
|
9
|
+
pytest-mock>=3.12.0
|
10
|
+
pytest-cov>=4.1.0
|
11
|
+
pytest-xdist>=3.5.0
|
12
|
+
pytest-asyncio>=0.24.0
|
13
|
+
mypy>=1.7.1
|
14
|
+
black[jupyter]<24.5.0,>=23.12.1
|
15
|
+
|
16
|
+
[docs]
|
17
|
+
|
18
|
+
[docs:python_version < "3.12"]
|
19
|
+
pydoc-markdown>=4.8.2
|
@@ -0,0 +1 @@
|
|
1
|
+
graphai
|
@@ -0,0 +1,32 @@
|
|
1
|
+
[project]
|
2
|
+
name = "graphai-lib"
|
3
|
+
version = "0.0.4"
|
4
|
+
description = "Not an AI framework"
|
5
|
+
readme = "README.md"
|
6
|
+
requires-python = ">=3.10,<3.14"
|
7
|
+
dependencies = [
|
8
|
+
"semantic-router>=0.1.5",
|
9
|
+
"networkx>=3.4.2",
|
10
|
+
"matplotlib>=3.10.0",
|
11
|
+
]
|
12
|
+
|
13
|
+
[project.optional-dependencies]
|
14
|
+
dev = [
|
15
|
+
"ipykernel>=6.25.0",
|
16
|
+
"ruff>=0.1.5",
|
17
|
+
"pytest>=8.2.0",
|
18
|
+
"pytest-mock>=3.12.0",
|
19
|
+
"pytest-cov>=4.1.0",
|
20
|
+
"pytest-xdist>=3.5.0",
|
21
|
+
"pytest-asyncio>=0.24.0",
|
22
|
+
"mypy>=1.7.1",
|
23
|
+
"black[jupyter]>=23.12.1,<24.5.0",
|
24
|
+
]
|
25
|
+
docs = ["pydoc-markdown>=4.8.2 ; python_version < '3.12'"]
|
26
|
+
|
27
|
+
[build-system]
|
28
|
+
requires = ["setuptools>=61.0"]
|
29
|
+
build-backend = "setuptools.build_meta"
|
30
|
+
|
31
|
+
[tool.setuptools]
|
32
|
+
packages = ["graphai", "graphai.nodes"]
|
graphai_lib-0.0.2/PKG-INFO
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.1
|
2
|
-
Name: graphai-lib
|
3
|
-
Version: 0.0.2
|
4
|
-
Summary:
|
5
|
-
License: MIT
|
6
|
-
Author: Aurelio AI
|
7
|
-
Author-email: hello@aurelio.ai
|
8
|
-
Requires-Python: >=3.10,<3.14
|
9
|
-
Classifier: License :: OSI Approved :: MIT License
|
10
|
-
Classifier: Programming Language :: Python :: 3
|
11
|
-
Classifier: Programming Language :: Python :: 3.10
|
12
|
-
Classifier: Programming Language :: Python :: 3.11
|
13
|
-
Classifier: Programming Language :: Python :: 3.12
|
14
|
-
Classifier: Programming Language :: Python :: 3.13
|
15
|
-
Requires-Dist: matplotlib (>=3.10.0,<4.0.0)
|
16
|
-
Requires-Dist: networkx (>=3.4.2,<4.0.0)
|
17
|
-
Requires-Dist: semantic-router (>=0.1.0.dev4)
|
18
|
-
Description-Content-Type: text/markdown
|
19
|
-
|
20
|
-
# Philosophy
|
21
|
-
|
22
|
-
1. Async-first
|
23
|
-
2. Minimize abstractions
|
24
|
-
3. One way to do one thing
|
25
|
-
4. Graph-based AI
|
graphai_lib-0.0.2/pyproject.toml
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
[tool.poetry]
|
2
|
-
name = "graphai-lib"
|
3
|
-
version = "0.0.2"
|
4
|
-
description = ""
|
5
|
-
authors = ["Aurelio AI <hello@aurelio.ai>"]
|
6
|
-
readme = "README.md"
|
7
|
-
packages = [{include = "graphai"}]
|
8
|
-
license = "MIT"
|
9
|
-
|
10
|
-
[tool.poetry.dependencies]
|
11
|
-
python = ">=3.10,<3.14"
|
12
|
-
semantic-router = ">=0.1.0.dev4"
|
13
|
-
networkx = "^3.4.2"
|
14
|
-
matplotlib = "^3.10.0"
|
15
|
-
|
16
|
-
[tool.poetry.group.dev.dependencies]
|
17
|
-
ipykernel = "^6.25.0"
|
18
|
-
ruff = "^0.1.5"
|
19
|
-
pytest = "^8.2"
|
20
|
-
pytest-mock = "^3.12.0"
|
21
|
-
pytest-cov = "^4.1.0"
|
22
|
-
pytest-xdist = "^3.5.0"
|
23
|
-
pytest-asyncio = "^0.24.0"
|
24
|
-
mypy = "^1.7.1"
|
25
|
-
black = {extras = ["jupyter"], version = ">=23.12.1,<24.5.0"}
|
26
|
-
|
27
|
-
[build-system]
|
28
|
-
requires = ["poetry-core"]
|
29
|
-
build-backend = "poetry.core.masonry.api"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|