schemez 1.1.1__tar.gz → 1.2.2__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.
- schemez-1.2.2/PKG-INFO +340 -0
- schemez-1.2.2/README.md +296 -0
- {schemez-1.1.1 → schemez-1.2.2}/pyproject.toml +9 -3
- {schemez-1.1.1 → schemez-1.2.2}/src/schemez/__init__.py +21 -0
- schemez-1.2.2/src/schemez/bind_kwargs.py +193 -0
- {schemez-1.1.1 → schemez-1.2.2}/src/schemez/convert.py +1 -1
- schemez-1.2.2/src/schemez/create_type.py +340 -0
- schemez-1.2.2/src/schemez/executable.py +211 -0
- schemez-1.2.2/src/schemez/functionschema.py +772 -0
- {schemez-1.1.1 → schemez-1.2.2}/src/schemez/helpers.py +9 -5
- schemez-1.2.2/src/schemez/log.py +17 -0
- schemez-1.2.2/src/schemez/schema_generators.py +215 -0
- schemez-1.2.2/src/schemez/tool_executor/__init__.py +8 -0
- schemez-1.2.2/src/schemez/tool_executor/executor.py +322 -0
- schemez-1.2.2/src/schemez/tool_executor/helpers.py +46 -0
- schemez-1.2.2/src/schemez/tool_executor/types.py +28 -0
- schemez-1.2.2/src/schemez/typedefs.py +205 -0
- schemez-1.1.1/PKG-INFO +0 -85
- schemez-1.1.1/README.md +0 -26
- {schemez-1.1.1 → schemez-1.2.2}/LICENSE +0 -0
- {schemez-1.1.1 → schemez-1.2.2}/src/schemez/code.py +0 -0
- {schemez-1.1.1 → schemez-1.2.2}/src/schemez/docstrings.py +0 -0
- {schemez-1.1.1 → schemez-1.2.2}/src/schemez/py.typed +0 -0
- {schemez-1.1.1 → schemez-1.2.2}/src/schemez/pydantic_types.py +0 -0
- {schemez-1.1.1 → schemez-1.2.2}/src/schemez/schema.py +0 -0
- {schemez-1.1.1 → schemez-1.2.2}/src/schemez/schemadef/__init__.py +0 -0
- {schemez-1.1.1 → schemez-1.2.2}/src/schemez/schemadef/schemadef.py +0 -0
schemez-1.2.2/PKG-INFO
ADDED
@@ -0,0 +1,340 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: schemez
|
3
|
+
Version: 1.2.2
|
4
|
+
Summary: Pydantic shim for config stuff
|
5
|
+
Keywords:
|
6
|
+
Author: Philipp Temminghoff
|
7
|
+
Author-email: Philipp Temminghoff <philipptemminghoff@googlemail.com>
|
8
|
+
License-Expression: MIT
|
9
|
+
License-File: LICENSE
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
11
|
+
Classifier: Framework :: Pydantic
|
12
|
+
Classifier: Framework :: Pydantic :: 2
|
13
|
+
Classifier: Intended Audience :: Developers
|
14
|
+
Classifier: Operating System :: OS Independent
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
20
|
+
Classifier: Topic :: Documentation
|
21
|
+
Classifier: Topic :: Software Development
|
22
|
+
Classifier: Topic :: Utilities
|
23
|
+
Classifier: Typing :: Typed
|
24
|
+
Requires-Dist: docstring-parser>=0.17.0
|
25
|
+
Requires-Dist: griffe>=1.7.3
|
26
|
+
Requires-Dist: pydantic
|
27
|
+
Requires-Dist: universal-pathlib>=0.2.6
|
28
|
+
Requires-Dist: llmling-agent ; extra == 'ai'
|
29
|
+
Requires-Dist: anyenv>=0.4.14 ; extra == 'ai'
|
30
|
+
Requires-Dist: datamodel-code-generator ; extra == 'codegen'
|
31
|
+
Requires-Dist: fastapi>=0.118.2 ; extra == 'tool-execution'
|
32
|
+
Requires-Dist: yamling ; extra == 'yaml'
|
33
|
+
Requires-Python: >=3.13
|
34
|
+
Project-URL: Code coverage, https://app.codecov.io/gh/phil65/schemez
|
35
|
+
Project-URL: Discussions, https://github.com/phil65/schemez/discussions
|
36
|
+
Project-URL: Documentation, https://phil65.github.io/schemez/
|
37
|
+
Project-URL: Issues, https://github.com/phil65/schemez/issues
|
38
|
+
Project-URL: Source, https://github.com/phil65/schemez
|
39
|
+
Provides-Extra: ai
|
40
|
+
Provides-Extra: codegen
|
41
|
+
Provides-Extra: tool-execution
|
42
|
+
Provides-Extra: yaml
|
43
|
+
Description-Content-Type: text/markdown
|
44
|
+
|
45
|
+
# Schemez
|
46
|
+
|
47
|
+
[](https://pypi.org/project/schemez/)
|
48
|
+
[](https://pypi.org/project/schemez/)
|
49
|
+
[](https://pypi.org/project/schemez/)
|
50
|
+
[](https://pypi.org/project/schemez/)
|
51
|
+
[](https://pypi.org/project/schemez/)
|
52
|
+
[](https://pypi.org/project/schemez/)
|
53
|
+
[](https://pypi.org/project/schemez/)
|
54
|
+
[](https://github.com/phil65/schemez/releases)
|
55
|
+
[](https://github.com/phil65/schemez/graphs/contributors)
|
56
|
+
[](https://github.com/phil65/schemez/discussions)
|
57
|
+
[](https://github.com/phil65/schemez/forks)
|
58
|
+
[](https://github.com/phil65/schemez/issues)
|
59
|
+
[](https://github.com/phil65/schemez/pulls)
|
60
|
+
[](https://github.com/phil65/schemez/watchers)
|
61
|
+
[](https://github.com/phil65/schemez/stars)
|
62
|
+
[](https://github.com/phil65/schemez)
|
63
|
+
[](https://github.com/phil65/schemez/commits)
|
64
|
+
[](https://github.com/phil65/schemez/releases)
|
65
|
+
[](https://github.com/phil65/schemez)
|
66
|
+
[](https://github.com/phil65/schemez)
|
67
|
+
[](https://codecov.io/gh/phil65/schemez/)
|
68
|
+
[](https://pyup.io/repos/github/phil65/schemez/)
|
69
|
+
|
70
|
+
[Read the documentation!](https://phil65.github.io/schemez/)
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
# OpenAI Function Schema Generator
|
75
|
+
|
76
|
+
Convert Python functions to OpenAI-compatible function schemas automatically.
|
77
|
+
|
78
|
+
## Installation
|
79
|
+
|
80
|
+
```bash
|
81
|
+
pip install schemez
|
82
|
+
```
|
83
|
+
|
84
|
+
## Basic Usage
|
85
|
+
|
86
|
+
```python
|
87
|
+
from schemez import create_schema
|
88
|
+
from typing import Literal
|
89
|
+
|
90
|
+
def get_weather(
|
91
|
+
location: str,
|
92
|
+
unit: Literal["C", "F"] = "C",
|
93
|
+
detailed: bool = False,
|
94
|
+
) -> dict[str, str | float]:
|
95
|
+
"""Get the weather for a location.
|
96
|
+
|
97
|
+
Args:
|
98
|
+
location: City or address to get weather for
|
99
|
+
unit: Temperature unit (Celsius or Fahrenheit)
|
100
|
+
detailed: Include extended forecast
|
101
|
+
"""
|
102
|
+
return {"temp": 22.5, "conditions": "sunny"}
|
103
|
+
|
104
|
+
# Create schema
|
105
|
+
schema = create_schema(get_weather)
|
106
|
+
|
107
|
+
# The schema.model_dump_openai() returns a TypedDict with the complete OpenAI tool definition:
|
108
|
+
# OpenAIFunctionTool = TypedDict({
|
109
|
+
# "type": Literal["function"],
|
110
|
+
# "function": OpenAIFunctionDefinition
|
111
|
+
# })
|
112
|
+
|
113
|
+
# Use with OpenAI
|
114
|
+
from openai import OpenAI
|
115
|
+
|
116
|
+
client = OpenAI()
|
117
|
+
response = client.chat.completions.create(
|
118
|
+
model="gpt-4",
|
119
|
+
messages=[{"role": "user", "content": "What's the weather in London?"}],
|
120
|
+
tools=[schema.model_dump_openai()], # Schema includes the type: "function" wrapper
|
121
|
+
tool_choice="auto"
|
122
|
+
)
|
123
|
+
```
|
124
|
+
|
125
|
+
> **Note**: This library supports the OpenAI API v1 format (openai>=1.0.0). For older
|
126
|
+
> versions of the OpenAI package that use the legacy functions API, you'll need to
|
127
|
+
> unwrap the function definition using `schema.model_dump_openai()["function"]`.
|
128
|
+
```
|
129
|
+
|
130
|
+
## Supported Types
|
131
|
+
|
132
|
+
### Basic Types
|
133
|
+
```python
|
134
|
+
def func(
|
135
|
+
text: str, # -> "type": "string"
|
136
|
+
number: int, # -> "type": "integer"
|
137
|
+
amount: float, # -> "type": "number"
|
138
|
+
enabled: bool, # -> "type": "boolean"
|
139
|
+
anything: Any, # -> "type": "string"
|
140
|
+
) -> None: ...
|
141
|
+
```
|
142
|
+
|
143
|
+
### Container Types
|
144
|
+
```python
|
145
|
+
def func(
|
146
|
+
items: list[str], # -> "type": "array", "items": {"type": "string"}
|
147
|
+
numbers: set[int], # -> same as list
|
148
|
+
mapping: dict[str, Any], # -> "type": "object", "additionalProperties": true
|
149
|
+
nested: list[dict[str, int]], # -> nested array/object types
|
150
|
+
sequence: Sequence[str], # -> "type": "array"
|
151
|
+
collection: Collection[int], # -> "type": "array"
|
152
|
+
) -> None: ...
|
153
|
+
```
|
154
|
+
|
155
|
+
### Enums and Literals
|
156
|
+
```python
|
157
|
+
class Color(Enum):
|
158
|
+
RED = "red"
|
159
|
+
BLUE = "blue"
|
160
|
+
|
161
|
+
def func(
|
162
|
+
color: Color, # -> "type": "string", "enum": ["red", "blue"]
|
163
|
+
mode: Literal["fast", "slow"], # -> "type": "string", "enum": ["fast", "slow"]
|
164
|
+
) -> None: ...
|
165
|
+
```
|
166
|
+
|
167
|
+
### Optional and Union Types
|
168
|
+
```python
|
169
|
+
def func(
|
170
|
+
opt1: str | None, # -> "type": "string"
|
171
|
+
opt2: int | None, # -> "type": "integer"
|
172
|
+
union: str | int, # -> "type": "string" (first type)
|
173
|
+
) -> None: ...
|
174
|
+
```
|
175
|
+
|
176
|
+
### Custom Types
|
177
|
+
```python
|
178
|
+
@dataclass
|
179
|
+
class User:
|
180
|
+
name: str
|
181
|
+
age: int
|
182
|
+
|
183
|
+
def func(
|
184
|
+
user: User, # -> "type": "object"
|
185
|
+
data: JsonDict, # -> "type": "object"
|
186
|
+
) -> None: ...
|
187
|
+
```
|
188
|
+
|
189
|
+
### Type Aliases
|
190
|
+
```python
|
191
|
+
JsonValue = dict[str, Any] | list[Any] | str | int | float | bool | None
|
192
|
+
JsonDict = dict[str, JsonValue]
|
193
|
+
|
194
|
+
def func(
|
195
|
+
data: JsonDict, # -> "type": "object"
|
196
|
+
values: list[JsonValue], # -> "type": "array"
|
197
|
+
) -> None: ...
|
198
|
+
```
|
199
|
+
|
200
|
+
### Recursive Types
|
201
|
+
```python
|
202
|
+
def func(
|
203
|
+
tree: dict[str, "dict[str, Any] | str"], # -> "type": "object"
|
204
|
+
nested: dict[str, list["dict[str, Any]"]], # -> "type": "object"
|
205
|
+
) -> None: ...
|
206
|
+
```
|
207
|
+
|
208
|
+
## Generated Schema Example
|
209
|
+
|
210
|
+
```python
|
211
|
+
{
|
212
|
+
"type": "function",
|
213
|
+
"function": {
|
214
|
+
"name": "get_weather",
|
215
|
+
"description": "Get the weather for a location.",
|
216
|
+
"parameters": {
|
217
|
+
"type": "object",
|
218
|
+
"properties": {
|
219
|
+
"location": {
|
220
|
+
"type": "string",
|
221
|
+
"description": "City or address to get weather for"
|
222
|
+
},
|
223
|
+
"unit": {
|
224
|
+
"type": "string",
|
225
|
+
"enum": ["C", "F"],
|
226
|
+
"description": "Temperature unit (Celsius or Fahrenheit)",
|
227
|
+
"default": "C"
|
228
|
+
},
|
229
|
+
"detailed": {
|
230
|
+
"type": "boolean",
|
231
|
+
"description": "Include extended forecast",
|
232
|
+
"default": false
|
233
|
+
}
|
234
|
+
},
|
235
|
+
"required": ["location"]
|
236
|
+
}
|
237
|
+
}
|
238
|
+
}
|
239
|
+
```
|
240
|
+
|
241
|
+
## Schema Generators
|
242
|
+
|
243
|
+
### Module Schemas
|
244
|
+
|
245
|
+
You can generate schemas for all public functions in a module using `create_schemas_from_module`:
|
246
|
+
|
247
|
+
```python
|
248
|
+
from schemez import create_schemas_from_module
|
249
|
+
import math
|
250
|
+
|
251
|
+
# Generate schemas for all public functions
|
252
|
+
schemas = create_schemas_from_module(math)
|
253
|
+
|
254
|
+
# Generate schemas for specific functions only
|
255
|
+
schemas = create_schemas_from_module(math, include_functions=['sin', 'cos'])
|
256
|
+
|
257
|
+
# Import module by string name
|
258
|
+
schemas = create_schemas_from_module('math')
|
259
|
+
```
|
260
|
+
|
261
|
+
### Class Schemas
|
262
|
+
|
263
|
+
Generate schemas for all public methods in a class using `create_schemas_from_class`:
|
264
|
+
|
265
|
+
```python
|
266
|
+
from schemez import create_schemas_from_class
|
267
|
+
|
268
|
+
class Calculator:
|
269
|
+
def add(self, x: int, y: int) -> int:
|
270
|
+
"""Add two numbers.
|
271
|
+
|
272
|
+
Args:
|
273
|
+
x: First number
|
274
|
+
y: Second number
|
275
|
+
|
276
|
+
Returns:
|
277
|
+
Sum of x and y
|
278
|
+
"""
|
279
|
+
return x + y
|
280
|
+
|
281
|
+
@classmethod
|
282
|
+
def multiply(cls, x: int, y: int) -> int:
|
283
|
+
"""Multiply two numbers.
|
284
|
+
|
285
|
+
Args:
|
286
|
+
x: First number
|
287
|
+
y: Second number
|
288
|
+
|
289
|
+
Returns:
|
290
|
+
Product of x and y
|
291
|
+
"""
|
292
|
+
return x * y
|
293
|
+
|
294
|
+
@staticmethod
|
295
|
+
def divide(x: float, y: float) -> float:
|
296
|
+
"""Divide two numbers.
|
297
|
+
|
298
|
+
Args:
|
299
|
+
x: Numerator
|
300
|
+
y: Denominator
|
301
|
+
|
302
|
+
Returns:
|
303
|
+
Result of x divided by y
|
304
|
+
"""
|
305
|
+
return x / y
|
306
|
+
|
307
|
+
# Generate schemas for all public methods
|
308
|
+
schemas = create_schemas_from_class(Calculator)
|
309
|
+
|
310
|
+
# Access individual method schemas
|
311
|
+
add_schema = schemas['Calculator.add']
|
312
|
+
multiply_schema = schemas['Calculator.multiply']
|
313
|
+
divide_schema = schemas['Calculator.divide']
|
314
|
+
```
|
315
|
+
|
316
|
+
The schema generators support:
|
317
|
+
|
318
|
+
- Regular functions
|
319
|
+
- Regular instance methods (bound and unbound)
|
320
|
+
- Class methods
|
321
|
+
- Static methods
|
322
|
+
- Decorated functions / methods
|
323
|
+
- Async functions / methods
|
324
|
+
- Property methods
|
325
|
+
- Basically all stdlib typing features as well as many stdlib types
|
326
|
+
- Method docstrings for descriptions
|
327
|
+
- Default values
|
328
|
+
- Return type hints
|
329
|
+
|
330
|
+
|
331
|
+
## Diferences to pydantic schema generation
|
332
|
+
|
333
|
+
While Pydantics schema generation preserves detailed type information, `schema.model_dump_openai()`
|
334
|
+
simplifies types to match OpenAI's function calling format. Most special types
|
335
|
+
(datetime, UUID, Path, etc.) are handled similarly by both (we only strip unused information), but we handle enums
|
336
|
+
differently: Instead of preserving enum class information, we extract just the values
|
337
|
+
as a string enum. Union types and Optionals are also handled differently - we typically
|
338
|
+
pick the first type to keep the schema simple and practical for AI interaction.
|
339
|
+
This ensures compatibility with OpenAI's function calling API while maintaining enough
|
340
|
+
type information for the AI to understand the function signature.
|
schemez-1.2.2/README.md
ADDED
@@ -0,0 +1,296 @@
|
|
1
|
+
# Schemez
|
2
|
+
|
3
|
+
[](https://pypi.org/project/schemez/)
|
4
|
+
[](https://pypi.org/project/schemez/)
|
5
|
+
[](https://pypi.org/project/schemez/)
|
6
|
+
[](https://pypi.org/project/schemez/)
|
7
|
+
[](https://pypi.org/project/schemez/)
|
8
|
+
[](https://pypi.org/project/schemez/)
|
9
|
+
[](https://pypi.org/project/schemez/)
|
10
|
+
[](https://github.com/phil65/schemez/releases)
|
11
|
+
[](https://github.com/phil65/schemez/graphs/contributors)
|
12
|
+
[](https://github.com/phil65/schemez/discussions)
|
13
|
+
[](https://github.com/phil65/schemez/forks)
|
14
|
+
[](https://github.com/phil65/schemez/issues)
|
15
|
+
[](https://github.com/phil65/schemez/pulls)
|
16
|
+
[](https://github.com/phil65/schemez/watchers)
|
17
|
+
[](https://github.com/phil65/schemez/stars)
|
18
|
+
[](https://github.com/phil65/schemez)
|
19
|
+
[](https://github.com/phil65/schemez/commits)
|
20
|
+
[](https://github.com/phil65/schemez/releases)
|
21
|
+
[](https://github.com/phil65/schemez)
|
22
|
+
[](https://github.com/phil65/schemez)
|
23
|
+
[](https://codecov.io/gh/phil65/schemez/)
|
24
|
+
[](https://pyup.io/repos/github/phil65/schemez/)
|
25
|
+
|
26
|
+
[Read the documentation!](https://phil65.github.io/schemez/)
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
# OpenAI Function Schema Generator
|
31
|
+
|
32
|
+
Convert Python functions to OpenAI-compatible function schemas automatically.
|
33
|
+
|
34
|
+
## Installation
|
35
|
+
|
36
|
+
```bash
|
37
|
+
pip install schemez
|
38
|
+
```
|
39
|
+
|
40
|
+
## Basic Usage
|
41
|
+
|
42
|
+
```python
|
43
|
+
from schemez import create_schema
|
44
|
+
from typing import Literal
|
45
|
+
|
46
|
+
def get_weather(
|
47
|
+
location: str,
|
48
|
+
unit: Literal["C", "F"] = "C",
|
49
|
+
detailed: bool = False,
|
50
|
+
) -> dict[str, str | float]:
|
51
|
+
"""Get the weather for a location.
|
52
|
+
|
53
|
+
Args:
|
54
|
+
location: City or address to get weather for
|
55
|
+
unit: Temperature unit (Celsius or Fahrenheit)
|
56
|
+
detailed: Include extended forecast
|
57
|
+
"""
|
58
|
+
return {"temp": 22.5, "conditions": "sunny"}
|
59
|
+
|
60
|
+
# Create schema
|
61
|
+
schema = create_schema(get_weather)
|
62
|
+
|
63
|
+
# The schema.model_dump_openai() returns a TypedDict with the complete OpenAI tool definition:
|
64
|
+
# OpenAIFunctionTool = TypedDict({
|
65
|
+
# "type": Literal["function"],
|
66
|
+
# "function": OpenAIFunctionDefinition
|
67
|
+
# })
|
68
|
+
|
69
|
+
# Use with OpenAI
|
70
|
+
from openai import OpenAI
|
71
|
+
|
72
|
+
client = OpenAI()
|
73
|
+
response = client.chat.completions.create(
|
74
|
+
model="gpt-4",
|
75
|
+
messages=[{"role": "user", "content": "What's the weather in London?"}],
|
76
|
+
tools=[schema.model_dump_openai()], # Schema includes the type: "function" wrapper
|
77
|
+
tool_choice="auto"
|
78
|
+
)
|
79
|
+
```
|
80
|
+
|
81
|
+
> **Note**: This library supports the OpenAI API v1 format (openai>=1.0.0). For older
|
82
|
+
> versions of the OpenAI package that use the legacy functions API, you'll need to
|
83
|
+
> unwrap the function definition using `schema.model_dump_openai()["function"]`.
|
84
|
+
```
|
85
|
+
|
86
|
+
## Supported Types
|
87
|
+
|
88
|
+
### Basic Types
|
89
|
+
```python
|
90
|
+
def func(
|
91
|
+
text: str, # -> "type": "string"
|
92
|
+
number: int, # -> "type": "integer"
|
93
|
+
amount: float, # -> "type": "number"
|
94
|
+
enabled: bool, # -> "type": "boolean"
|
95
|
+
anything: Any, # -> "type": "string"
|
96
|
+
) -> None: ...
|
97
|
+
```
|
98
|
+
|
99
|
+
### Container Types
|
100
|
+
```python
|
101
|
+
def func(
|
102
|
+
items: list[str], # -> "type": "array", "items": {"type": "string"}
|
103
|
+
numbers: set[int], # -> same as list
|
104
|
+
mapping: dict[str, Any], # -> "type": "object", "additionalProperties": true
|
105
|
+
nested: list[dict[str, int]], # -> nested array/object types
|
106
|
+
sequence: Sequence[str], # -> "type": "array"
|
107
|
+
collection: Collection[int], # -> "type": "array"
|
108
|
+
) -> None: ...
|
109
|
+
```
|
110
|
+
|
111
|
+
### Enums and Literals
|
112
|
+
```python
|
113
|
+
class Color(Enum):
|
114
|
+
RED = "red"
|
115
|
+
BLUE = "blue"
|
116
|
+
|
117
|
+
def func(
|
118
|
+
color: Color, # -> "type": "string", "enum": ["red", "blue"]
|
119
|
+
mode: Literal["fast", "slow"], # -> "type": "string", "enum": ["fast", "slow"]
|
120
|
+
) -> None: ...
|
121
|
+
```
|
122
|
+
|
123
|
+
### Optional and Union Types
|
124
|
+
```python
|
125
|
+
def func(
|
126
|
+
opt1: str | None, # -> "type": "string"
|
127
|
+
opt2: int | None, # -> "type": "integer"
|
128
|
+
union: str | int, # -> "type": "string" (first type)
|
129
|
+
) -> None: ...
|
130
|
+
```
|
131
|
+
|
132
|
+
### Custom Types
|
133
|
+
```python
|
134
|
+
@dataclass
|
135
|
+
class User:
|
136
|
+
name: str
|
137
|
+
age: int
|
138
|
+
|
139
|
+
def func(
|
140
|
+
user: User, # -> "type": "object"
|
141
|
+
data: JsonDict, # -> "type": "object"
|
142
|
+
) -> None: ...
|
143
|
+
```
|
144
|
+
|
145
|
+
### Type Aliases
|
146
|
+
```python
|
147
|
+
JsonValue = dict[str, Any] | list[Any] | str | int | float | bool | None
|
148
|
+
JsonDict = dict[str, JsonValue]
|
149
|
+
|
150
|
+
def func(
|
151
|
+
data: JsonDict, # -> "type": "object"
|
152
|
+
values: list[JsonValue], # -> "type": "array"
|
153
|
+
) -> None: ...
|
154
|
+
```
|
155
|
+
|
156
|
+
### Recursive Types
|
157
|
+
```python
|
158
|
+
def func(
|
159
|
+
tree: dict[str, "dict[str, Any] | str"], # -> "type": "object"
|
160
|
+
nested: dict[str, list["dict[str, Any]"]], # -> "type": "object"
|
161
|
+
) -> None: ...
|
162
|
+
```
|
163
|
+
|
164
|
+
## Generated Schema Example
|
165
|
+
|
166
|
+
```python
|
167
|
+
{
|
168
|
+
"type": "function",
|
169
|
+
"function": {
|
170
|
+
"name": "get_weather",
|
171
|
+
"description": "Get the weather for a location.",
|
172
|
+
"parameters": {
|
173
|
+
"type": "object",
|
174
|
+
"properties": {
|
175
|
+
"location": {
|
176
|
+
"type": "string",
|
177
|
+
"description": "City or address to get weather for"
|
178
|
+
},
|
179
|
+
"unit": {
|
180
|
+
"type": "string",
|
181
|
+
"enum": ["C", "F"],
|
182
|
+
"description": "Temperature unit (Celsius or Fahrenheit)",
|
183
|
+
"default": "C"
|
184
|
+
},
|
185
|
+
"detailed": {
|
186
|
+
"type": "boolean",
|
187
|
+
"description": "Include extended forecast",
|
188
|
+
"default": false
|
189
|
+
}
|
190
|
+
},
|
191
|
+
"required": ["location"]
|
192
|
+
}
|
193
|
+
}
|
194
|
+
}
|
195
|
+
```
|
196
|
+
|
197
|
+
## Schema Generators
|
198
|
+
|
199
|
+
### Module Schemas
|
200
|
+
|
201
|
+
You can generate schemas for all public functions in a module using `create_schemas_from_module`:
|
202
|
+
|
203
|
+
```python
|
204
|
+
from schemez import create_schemas_from_module
|
205
|
+
import math
|
206
|
+
|
207
|
+
# Generate schemas for all public functions
|
208
|
+
schemas = create_schemas_from_module(math)
|
209
|
+
|
210
|
+
# Generate schemas for specific functions only
|
211
|
+
schemas = create_schemas_from_module(math, include_functions=['sin', 'cos'])
|
212
|
+
|
213
|
+
# Import module by string name
|
214
|
+
schemas = create_schemas_from_module('math')
|
215
|
+
```
|
216
|
+
|
217
|
+
### Class Schemas
|
218
|
+
|
219
|
+
Generate schemas for all public methods in a class using `create_schemas_from_class`:
|
220
|
+
|
221
|
+
```python
|
222
|
+
from schemez import create_schemas_from_class
|
223
|
+
|
224
|
+
class Calculator:
|
225
|
+
def add(self, x: int, y: int) -> int:
|
226
|
+
"""Add two numbers.
|
227
|
+
|
228
|
+
Args:
|
229
|
+
x: First number
|
230
|
+
y: Second number
|
231
|
+
|
232
|
+
Returns:
|
233
|
+
Sum of x and y
|
234
|
+
"""
|
235
|
+
return x + y
|
236
|
+
|
237
|
+
@classmethod
|
238
|
+
def multiply(cls, x: int, y: int) -> int:
|
239
|
+
"""Multiply two numbers.
|
240
|
+
|
241
|
+
Args:
|
242
|
+
x: First number
|
243
|
+
y: Second number
|
244
|
+
|
245
|
+
Returns:
|
246
|
+
Product of x and y
|
247
|
+
"""
|
248
|
+
return x * y
|
249
|
+
|
250
|
+
@staticmethod
|
251
|
+
def divide(x: float, y: float) -> float:
|
252
|
+
"""Divide two numbers.
|
253
|
+
|
254
|
+
Args:
|
255
|
+
x: Numerator
|
256
|
+
y: Denominator
|
257
|
+
|
258
|
+
Returns:
|
259
|
+
Result of x divided by y
|
260
|
+
"""
|
261
|
+
return x / y
|
262
|
+
|
263
|
+
# Generate schemas for all public methods
|
264
|
+
schemas = create_schemas_from_class(Calculator)
|
265
|
+
|
266
|
+
# Access individual method schemas
|
267
|
+
add_schema = schemas['Calculator.add']
|
268
|
+
multiply_schema = schemas['Calculator.multiply']
|
269
|
+
divide_schema = schemas['Calculator.divide']
|
270
|
+
```
|
271
|
+
|
272
|
+
The schema generators support:
|
273
|
+
|
274
|
+
- Regular functions
|
275
|
+
- Regular instance methods (bound and unbound)
|
276
|
+
- Class methods
|
277
|
+
- Static methods
|
278
|
+
- Decorated functions / methods
|
279
|
+
- Async functions / methods
|
280
|
+
- Property methods
|
281
|
+
- Basically all stdlib typing features as well as many stdlib types
|
282
|
+
- Method docstrings for descriptions
|
283
|
+
- Default values
|
284
|
+
- Return type hints
|
285
|
+
|
286
|
+
|
287
|
+
## Diferences to pydantic schema generation
|
288
|
+
|
289
|
+
While Pydantics schema generation preserves detailed type information, `schema.model_dump_openai()`
|
290
|
+
simplifies types to match OpenAI's function calling format. Most special types
|
291
|
+
(datetime, UUID, Path, etc.) are handled similarly by both (we only strip unused information), but we handle enums
|
292
|
+
differently: Instead of preserving enum class information, we extract just the values
|
293
|
+
as a string enum. Union types and Optionals are also handled differently - we typically
|
294
|
+
pick the first type to keep the schema simple and practical for AI interaction.
|
295
|
+
This ensures compatibility with OpenAI's function calling API while maintaining enough
|
296
|
+
type information for the AI to understand the function signature.
|
@@ -1,10 +1,11 @@
|
|
1
1
|
[project]
|
2
2
|
name = "schemez"
|
3
|
-
version = "1.
|
3
|
+
version = "1.2.2"
|
4
4
|
description = "Pydantic shim for config stuff"
|
5
5
|
readme = "README.md"
|
6
6
|
requires-python = ">=3.13"
|
7
|
-
license =
|
7
|
+
license = "MIT"
|
8
|
+
license-files = ["LICENSE"]
|
8
9
|
authors = [
|
9
10
|
{ name = "Philipp Temminghoff", email = "philipptemminghoff@googlemail.com" },
|
10
11
|
]
|
@@ -26,6 +27,7 @@ classifiers = [
|
|
26
27
|
"Typing :: Typed",
|
27
28
|
]
|
28
29
|
dependencies = [
|
30
|
+
"docstring-parser>=0.17.0",
|
29
31
|
"griffe>=1.7.3",
|
30
32
|
"pydantic",
|
31
33
|
# Only add below (Copier)
|
@@ -41,6 +43,8 @@ Source = "https://github.com/phil65/schemez"
|
|
41
43
|
|
42
44
|
[project.optional-dependencies]
|
43
45
|
ai = ["llmling-agent", "anyenv>=0.4.14"]
|
46
|
+
codegen = ["datamodel-code-generator"]
|
47
|
+
tool_execution = ["fastapi>=0.118.2"]
|
44
48
|
yaml = ["yamling"]
|
45
49
|
|
46
50
|
[dependency-groups]
|
@@ -122,6 +126,7 @@ log_cli = true
|
|
122
126
|
log_date_format = "%Y-%m-%d %H:%M:%S"
|
123
127
|
log_format = "%(asctime)s %(levelname)s %(message)s"
|
124
128
|
testpaths = "tests/"
|
129
|
+
asyncio_mode = "auto"
|
125
130
|
|
126
131
|
[tool.ruff]
|
127
132
|
line-length = 90
|
@@ -256,13 +261,14 @@ combine-as-imports = true
|
|
256
261
|
[tool.ruff.lint.per-file-ignores]
|
257
262
|
"__init__.py" = ["E402", "I001"]
|
258
263
|
"scripts/*" = ["INP001"]
|
264
|
+
"*tests/*" = ["D100"]
|
259
265
|
|
260
266
|
[tool.ruff.format]
|
261
|
-
# Enable preview style formatting.
|
262
267
|
preview = true
|
263
268
|
|
264
269
|
[tool.ty.environment]
|
265
270
|
python-version = "3.13"
|
271
|
+
python-platform = "all"
|
266
272
|
|
267
273
|
[tool.uv]
|
268
274
|
default-groups = ["dev", "lint", "docs"]
|