schemez 1.1.1__py3-none-any.whl → 1.2.2__py3-none-any.whl
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.
- schemez/__init__.py +21 -0
- schemez/bind_kwargs.py +193 -0
- schemez/convert.py +1 -1
- schemez/create_type.py +340 -0
- schemez/executable.py +211 -0
- schemez/functionschema.py +772 -0
- schemez/helpers.py +9 -5
- schemez/log.py +17 -0
- schemez/schema_generators.py +215 -0
- schemez/tool_executor/__init__.py +8 -0
- schemez/tool_executor/executor.py +322 -0
- schemez/tool_executor/helpers.py +46 -0
- schemez/tool_executor/types.py +28 -0
- schemez/typedefs.py +205 -0
- schemez-1.2.2.dist-info/METADATA +340 -0
- schemez-1.2.2.dist-info/RECORD +25 -0
- {schemez-1.1.1.dist-info → schemez-1.2.2.dist-info}/WHEEL +1 -1
- schemez-1.2.2.dist-info/licenses/LICENSE +22 -0
- schemez-1.1.1.dist-info/METADATA +0 -85
- schemez-1.1.1.dist-info/RECORD +0 -13
schemez/executable.py
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import asyncio
|
4
|
+
from collections.abc import AsyncIterator, Callable # noqa: TC003
|
5
|
+
from typing import TYPE_CHECKING, Any, TypeVar, overload
|
6
|
+
|
7
|
+
from schemez.functionschema import FunctionType, create_schema
|
8
|
+
|
9
|
+
|
10
|
+
if TYPE_CHECKING:
|
11
|
+
from collections.abc import AsyncGenerator, Generator
|
12
|
+
|
13
|
+
from schemez.functionschema import FunctionSchema
|
14
|
+
|
15
|
+
T_co = TypeVar("T_co", covariant=True)
|
16
|
+
|
17
|
+
|
18
|
+
class ExecutableFunction[T_co]:
|
19
|
+
"""Wrapper for executing functions with different calling patterns."""
|
20
|
+
|
21
|
+
def __init__(
|
22
|
+
self,
|
23
|
+
schema: FunctionSchema,
|
24
|
+
func: (
|
25
|
+
Callable[..., T_co]
|
26
|
+
| Callable[..., Generator[T_co]]
|
27
|
+
| Callable[..., AsyncGenerator[T_co]]
|
28
|
+
| Callable[..., AsyncIterator[T_co]]
|
29
|
+
),
|
30
|
+
) -> None:
|
31
|
+
"""Initialize with schema and function.
|
32
|
+
|
33
|
+
Args:
|
34
|
+
schema: OpenAI function schema
|
35
|
+
func: The actual function to execute
|
36
|
+
"""
|
37
|
+
self.schema = schema
|
38
|
+
self.func = func
|
39
|
+
|
40
|
+
def run(self, *args: Any, **kwargs: Any) -> T_co | list[T_co]: # noqa: PLR0911
|
41
|
+
"""Run the function synchronously.
|
42
|
+
|
43
|
+
Args:
|
44
|
+
*args: Positional arguments to pass to the function
|
45
|
+
**kwargs: Keyword arguments to pass to the function
|
46
|
+
|
47
|
+
Returns:
|
48
|
+
Either a single result or list of results for generators
|
49
|
+
"""
|
50
|
+
match self.schema.function_type:
|
51
|
+
case FunctionType.SYNC:
|
52
|
+
return self.func(*args, **kwargs) # type: ignore
|
53
|
+
case FunctionType.ASYNC:
|
54
|
+
try:
|
55
|
+
loop = asyncio.get_running_loop()
|
56
|
+
except RuntimeError:
|
57
|
+
return asyncio.run(self.func(*args, **kwargs)) # type: ignore
|
58
|
+
else:
|
59
|
+
if loop.is_running():
|
60
|
+
new_loop = asyncio.new_event_loop()
|
61
|
+
try:
|
62
|
+
return new_loop.run_until_complete(
|
63
|
+
self.func(*args, **kwargs), # type: ignore
|
64
|
+
)
|
65
|
+
finally:
|
66
|
+
new_loop.close()
|
67
|
+
return loop.run_until_complete(
|
68
|
+
self.func(*args, **kwargs), # type: ignore
|
69
|
+
)
|
70
|
+
case FunctionType.SYNC_GENERATOR:
|
71
|
+
return list(self.func(*args, **kwargs)) # type: ignore
|
72
|
+
case FunctionType.ASYNC_GENERATOR:
|
73
|
+
try:
|
74
|
+
loop = asyncio.get_running_loop()
|
75
|
+
except RuntimeError:
|
76
|
+
return asyncio.run(self._collect_async_gen(*args, **kwargs))
|
77
|
+
else:
|
78
|
+
if loop.is_running():
|
79
|
+
new_loop = asyncio.new_event_loop()
|
80
|
+
try:
|
81
|
+
return new_loop.run_until_complete(
|
82
|
+
self._collect_async_gen(*args, **kwargs),
|
83
|
+
)
|
84
|
+
finally:
|
85
|
+
new_loop.close()
|
86
|
+
return loop.run_until_complete(
|
87
|
+
self._collect_async_gen(*args, **kwargs),
|
88
|
+
)
|
89
|
+
case _:
|
90
|
+
msg = f"Unknown function type: {self.schema.function_type}"
|
91
|
+
raise ValueError(msg)
|
92
|
+
|
93
|
+
async def _collect_async_gen(self, *args: Any, **kwargs: Any) -> list[T_co]:
|
94
|
+
"""Collect async generator results into a list.
|
95
|
+
|
96
|
+
Args:
|
97
|
+
*args: Positional arguments to pass to the function
|
98
|
+
**kwargs: Keyword arguments to pass to the function
|
99
|
+
|
100
|
+
Returns:
|
101
|
+
List of collected results
|
102
|
+
"""
|
103
|
+
return [x async for x in self.func(*args, **kwargs)] # type: ignore
|
104
|
+
|
105
|
+
async def arun(self, *args: Any, **kwargs: Any) -> T_co | list[T_co]:
|
106
|
+
"""Run the function asynchronously.
|
107
|
+
|
108
|
+
Args:
|
109
|
+
*args: Positional arguments to pass to the function
|
110
|
+
**kwargs: Keyword arguments to pass to the function
|
111
|
+
|
112
|
+
Returns:
|
113
|
+
Function result or list of results for generators
|
114
|
+
|
115
|
+
Raises:
|
116
|
+
ValueError: If the function type is unknown
|
117
|
+
"""
|
118
|
+
match self.schema.function_type:
|
119
|
+
case FunctionType.SYNC:
|
120
|
+
return self.func(*args, **kwargs) # type: ignore
|
121
|
+
case FunctionType.ASYNC:
|
122
|
+
return await self.func(*args, **kwargs) # type: ignore
|
123
|
+
case FunctionType.SYNC_GENERATOR:
|
124
|
+
return list(self.func(*args, **kwargs)) # type: ignore
|
125
|
+
case FunctionType.ASYNC_GENERATOR:
|
126
|
+
return [x async for x in self.func(*args, **kwargs)] # type: ignore
|
127
|
+
case _:
|
128
|
+
msg = f"Unknown function type: {self.schema.function_type}"
|
129
|
+
raise ValueError(msg)
|
130
|
+
|
131
|
+
async def astream(self, *args: Any, **kwargs: Any) -> AsyncIterator[T_co]:
|
132
|
+
"""Stream results from the function.
|
133
|
+
|
134
|
+
Args:
|
135
|
+
*args: Positional arguments to pass to the function
|
136
|
+
**kwargs: Keyword arguments to pass to the function
|
137
|
+
|
138
|
+
Yields:
|
139
|
+
Individual results as they become available
|
140
|
+
|
141
|
+
Raises:
|
142
|
+
ValueError: If the function type is unknown
|
143
|
+
"""
|
144
|
+
match self.schema.function_type:
|
145
|
+
case FunctionType.SYNC_GENERATOR:
|
146
|
+
for x in self.func(*args, **kwargs): # type: ignore
|
147
|
+
yield x
|
148
|
+
case FunctionType.ASYNC_GENERATOR:
|
149
|
+
async for x in self.func(*args, **kwargs): # type: ignore
|
150
|
+
yield x
|
151
|
+
case FunctionType.SYNC:
|
152
|
+
yield self.func(*args, **kwargs) # type: ignore
|
153
|
+
case FunctionType.ASYNC:
|
154
|
+
yield await self.func(*args, **kwargs) # type: ignore
|
155
|
+
case _:
|
156
|
+
msg = f"Unknown function type: {self.schema.function_type}"
|
157
|
+
raise ValueError(msg)
|
158
|
+
|
159
|
+
|
160
|
+
@overload
|
161
|
+
def create_executable[T_co](
|
162
|
+
func: Callable[..., T_co],
|
163
|
+
) -> ExecutableFunction[T_co]: ...
|
164
|
+
|
165
|
+
|
166
|
+
@overload
|
167
|
+
def create_executable[T_co](
|
168
|
+
func: Callable[..., Generator[T_co]],
|
169
|
+
) -> ExecutableFunction[T_co]: ...
|
170
|
+
|
171
|
+
|
172
|
+
@overload
|
173
|
+
def create_executable[T_co](
|
174
|
+
func: Callable[..., AsyncGenerator[T_co]],
|
175
|
+
) -> ExecutableFunction[T_co]: ...
|
176
|
+
|
177
|
+
|
178
|
+
def create_executable(
|
179
|
+
func: (
|
180
|
+
Callable[..., T_co]
|
181
|
+
| Callable[..., Generator[T_co]]
|
182
|
+
| Callable[..., AsyncGenerator[T_co]]
|
183
|
+
),
|
184
|
+
) -> ExecutableFunction[T_co]:
|
185
|
+
"""Create an executable function wrapper with schema.
|
186
|
+
|
187
|
+
Args:
|
188
|
+
func: Function to wrap
|
189
|
+
|
190
|
+
Returns:
|
191
|
+
Executable wrapper with schema
|
192
|
+
"""
|
193
|
+
schema = create_schema(func)
|
194
|
+
return ExecutableFunction(schema, func)
|
195
|
+
|
196
|
+
|
197
|
+
if __name__ == "__main__":
|
198
|
+
from typing import Literal
|
199
|
+
|
200
|
+
def get_weather(
|
201
|
+
location: str,
|
202
|
+
unit: Literal["C", "F"] = "C",
|
203
|
+
detailed: bool = False,
|
204
|
+
) -> dict[str, str | float]:
|
205
|
+
return {"temp": 22.5, "conditions": "sunny"}
|
206
|
+
|
207
|
+
exe = create_executable(get_weather)
|
208
|
+
# Execute the function
|
209
|
+
result = exe.run("London", unit="C")
|
210
|
+
print("\nFunction Result:")
|
211
|
+
print(result)
|