graphai-lib 0.0.3__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.3 → graphai_lib-0.0.4}/PKG-INFO +3 -1
- graphai_lib-0.0.4/graphai/nodes/__init__.py +3 -0
- graphai_lib-0.0.4/graphai/nodes/base.py +154 -0
- {graphai_lib-0.0.3 → graphai_lib-0.0.4}/graphai_lib.egg-info/PKG-INFO +3 -1
- {graphai_lib-0.0.3 → graphai_lib-0.0.4}/graphai_lib.egg-info/SOURCES.txt +2 -0
- {graphai_lib-0.0.3 → graphai_lib-0.0.4}/graphai_lib.egg-info/requires.txt +5 -0
- {graphai_lib-0.0.3 → graphai_lib-0.0.4}/pyproject.toml +4 -3
- {graphai_lib-0.0.3 → graphai_lib-0.0.4}/README.md +0 -0
- {graphai_lib-0.0.3 → graphai_lib-0.0.4}/graphai/__init__.py +0 -0
- {graphai_lib-0.0.3 → graphai_lib-0.0.4}/graphai/callback.py +0 -0
- {graphai_lib-0.0.3 → graphai_lib-0.0.4}/graphai/graph.py +0 -0
- {graphai_lib-0.0.3 → graphai_lib-0.0.4}/graphai/utils.py +0 -0
- {graphai_lib-0.0.3 → graphai_lib-0.0.4}/graphai_lib.egg-info/dependency_links.txt +0 -0
- {graphai_lib-0.0.3 → graphai_lib-0.0.4}/graphai_lib.egg-info/top_level.txt +0 -0
- {graphai_lib-0.0.3 → graphai_lib-0.0.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: graphai-lib
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.4
|
4
4
|
Summary: Not an AI framework
|
5
5
|
Requires-Python: <3.14,>=3.10
|
6
6
|
Description-Content-Type: text/markdown
|
@@ -17,6 +17,8 @@ Requires-Dist: pytest-xdist>=3.5.0; extra == "dev"
|
|
17
17
|
Requires-Dist: pytest-asyncio>=0.24.0; extra == "dev"
|
18
18
|
Requires-Dist: mypy>=1.7.1; extra == "dev"
|
19
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"
|
20
22
|
|
21
23
|
# Philosophy
|
22
24
|
|
@@ -0,0 +1,154 @@
|
|
1
|
+
import inspect
|
2
|
+
from typing import Any, Callable, Dict, Optional
|
3
|
+
|
4
|
+
from graphai.callback import Callback
|
5
|
+
from graphai.utils import FunctionSchema
|
6
|
+
|
7
|
+
|
8
|
+
class NodeMeta(type):
|
9
|
+
@staticmethod
|
10
|
+
def positional_to_kwargs(cls_type, args) -> Dict[str, Any]:
|
11
|
+
init_signature = inspect.signature(cls_type.__init__)
|
12
|
+
init_params = {name: arg for name, arg in init_signature.parameters.items() if name != "self"}
|
13
|
+
return init_params
|
14
|
+
|
15
|
+
def __call__(cls, *args, **kwargs):
|
16
|
+
named_positional_args = NodeMeta.positional_to_kwargs(cls, args)
|
17
|
+
kwargs.update(named_positional_args)
|
18
|
+
return super().__call__(**kwargs)
|
19
|
+
|
20
|
+
|
21
|
+
class _Node:
|
22
|
+
def __init__(
|
23
|
+
self,
|
24
|
+
is_router: bool = False,
|
25
|
+
):
|
26
|
+
self.is_router = is_router
|
27
|
+
|
28
|
+
def _node(
|
29
|
+
self,
|
30
|
+
func: Callable,
|
31
|
+
start: bool = False,
|
32
|
+
end: bool = False,
|
33
|
+
stream: bool = False,
|
34
|
+
name: str | None = None,
|
35
|
+
) -> Callable:
|
36
|
+
"""Decorator validating node structure.
|
37
|
+
"""
|
38
|
+
if not callable(func):
|
39
|
+
raise ValueError("Node must be a callable function.")
|
40
|
+
|
41
|
+
func_signature = inspect.signature(func)
|
42
|
+
schema = FunctionSchema(func)
|
43
|
+
|
44
|
+
class NodeClass:
|
45
|
+
_func_signature = func_signature
|
46
|
+
is_router = None
|
47
|
+
_stream = stream
|
48
|
+
|
49
|
+
def __init__(self):
|
50
|
+
self._expected_params = set(self._func_signature.parameters.keys())
|
51
|
+
|
52
|
+
async def execute(self, *args, **kwargs):
|
53
|
+
# Prepare arguments, including callback if stream is True
|
54
|
+
params_dict = await self._parse_params(*args, **kwargs)
|
55
|
+
return await func(**params_dict) # Pass only the necessary arguments
|
56
|
+
|
57
|
+
async def _parse_params(self, *args, **kwargs) -> Dict[str, Any]:
|
58
|
+
# filter out unexpected keyword args
|
59
|
+
expected_kwargs = {k: v for k, v in kwargs.items() if k in self._expected_params}
|
60
|
+
# Convert args to kwargs based on the function signature
|
61
|
+
args_names = list(self._func_signature.parameters.keys())[1:len(args)+1] # skip 'self'
|
62
|
+
expected_args_kwargs = dict(zip(args_names, args))
|
63
|
+
# Combine filtered args and kwargs
|
64
|
+
combined_params = {**expected_args_kwargs, **expected_kwargs}
|
65
|
+
|
66
|
+
# Bind the current instance attributes to the function signature
|
67
|
+
if "callback" in self._expected_params and not stream:
|
68
|
+
raise ValueError(
|
69
|
+
f"Node {func.__name__}: requires stream=True when callback is defined."
|
70
|
+
)
|
71
|
+
bound_params = self._func_signature.bind_partial(**combined_params)
|
72
|
+
# get the default parameters (if any)
|
73
|
+
bound_params.apply_defaults()
|
74
|
+
params_dict = bound_params.arguments.copy()
|
75
|
+
# Filter arguments to match the next node's parameters
|
76
|
+
filtered_params = {
|
77
|
+
k: v for k, v in params_dict.items() if k in self._expected_params
|
78
|
+
}
|
79
|
+
# confirm all required parameters are present
|
80
|
+
missing_params = [
|
81
|
+
p for p in self._expected_params if p not in filtered_params
|
82
|
+
]
|
83
|
+
# if anything is missing we raise an error
|
84
|
+
if missing_params:
|
85
|
+
raise ValueError(
|
86
|
+
f"Missing required parameters for the {func.__name__} node: {', '.join(missing_params)}"
|
87
|
+
)
|
88
|
+
return filtered_params
|
89
|
+
|
90
|
+
|
91
|
+
@classmethod
|
92
|
+
def get_signature(cls):
|
93
|
+
"""Returns the signature of the decorated function as LLM readable
|
94
|
+
string.
|
95
|
+
"""
|
96
|
+
signature_components = []
|
97
|
+
if NodeClass._func_signature:
|
98
|
+
for param in NodeClass._func_signature.parameters.values():
|
99
|
+
if param.default is param.empty:
|
100
|
+
signature_components.append(f"{param.name}: {param.annotation}")
|
101
|
+
else:
|
102
|
+
signature_components.append(f"{param.name}: {param.annotation} = {param.default}")
|
103
|
+
else:
|
104
|
+
return "No signature"
|
105
|
+
return "\n".join(signature_components)
|
106
|
+
|
107
|
+
@classmethod
|
108
|
+
async def invoke(cls, input: Dict[str, Any], callback: Optional[Callback] = None, state: Optional[Dict[str, Any]] = None):
|
109
|
+
if callback:
|
110
|
+
if stream:
|
111
|
+
input["callback"] = callback
|
112
|
+
else:
|
113
|
+
raise ValueError(
|
114
|
+
f"Error in node {func.__name__}. When callback provided, stream must be True."
|
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
|
+
|
120
|
+
instance = cls()
|
121
|
+
out = await instance.execute(**input)
|
122
|
+
return out
|
123
|
+
|
124
|
+
NodeClass.__name__ = func.__name__
|
125
|
+
NodeClass.name = name or func.__name__
|
126
|
+
NodeClass.__doc__ = func.__doc__
|
127
|
+
NodeClass.is_start = start
|
128
|
+
NodeClass.is_end = end
|
129
|
+
NodeClass.is_router = self.is_router
|
130
|
+
NodeClass.stream = stream
|
131
|
+
NodeClass.schema = schema
|
132
|
+
return NodeClass
|
133
|
+
|
134
|
+
def __call__(
|
135
|
+
self,
|
136
|
+
func: Optional[Callable] = None,
|
137
|
+
start: bool = False,
|
138
|
+
end: bool = False,
|
139
|
+
stream: bool = False,
|
140
|
+
name: str | None = None,
|
141
|
+
):
|
142
|
+
# We must wrap the call to the decorator in a function for it to work
|
143
|
+
# correctly with or without parenthesis
|
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)
|
146
|
+
if func:
|
147
|
+
# Decorator is called without parenthesis
|
148
|
+
return wrap(func=func, start=start, end=end, stream=stream, name=name)
|
149
|
+
# Decorator is called with parenthesis
|
150
|
+
return wrap
|
151
|
+
|
152
|
+
|
153
|
+
node = _Node()
|
154
|
+
router = _Node(is_router=True)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: graphai-lib
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.4
|
4
4
|
Summary: Not an AI framework
|
5
5
|
Requires-Python: <3.14,>=3.10
|
6
6
|
Description-Content-Type: text/markdown
|
@@ -17,6 +17,8 @@ Requires-Dist: pytest-xdist>=3.5.0; extra == "dev"
|
|
17
17
|
Requires-Dist: pytest-asyncio>=0.24.0; extra == "dev"
|
18
18
|
Requires-Dist: mypy>=1.7.1; extra == "dev"
|
19
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"
|
20
22
|
|
21
23
|
# Philosophy
|
22
24
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "graphai-lib"
|
3
|
-
version = "0.0.
|
3
|
+
version = "0.0.4"
|
4
4
|
description = "Not an AI framework"
|
5
5
|
readme = "README.md"
|
6
6
|
requires-python = ">=3.10,<3.14"
|
@@ -22,10 +22,11 @@ dev = [
|
|
22
22
|
"mypy>=1.7.1",
|
23
23
|
"black[jupyter]>=23.12.1,<24.5.0",
|
24
24
|
]
|
25
|
+
docs = ["pydoc-markdown>=4.8.2 ; python_version < '3.12'"]
|
25
26
|
|
26
27
|
[build-system]
|
27
28
|
requires = ["setuptools>=61.0"]
|
28
29
|
build-backend = "setuptools.build_meta"
|
29
30
|
|
30
|
-
[tool.setuptools
|
31
|
-
|
31
|
+
[tool.setuptools]
|
32
|
+
packages = ["graphai", "graphai.nodes"]
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|