matrix-fn-schema 0.1.0__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.
- matrix_fn_schema-0.1.0/LICENSE +21 -0
- matrix_fn_schema-0.1.0/PKG-INFO +56 -0
- matrix_fn_schema-0.1.0/README.md +36 -0
- matrix_fn_schema-0.1.0/pyproject.toml +33 -0
- matrix_fn_schema-0.1.0/setup.cfg +4 -0
- matrix_fn_schema-0.1.0/src/matrix_fn_schema/__init__.py +5 -0
- matrix_fn_schema-0.1.0/src/matrix_fn_schema/py.typed +1 -0
- matrix_fn_schema-0.1.0/src/matrix_fn_schema/schema.py +175 -0
- matrix_fn_schema-0.1.0/src/matrix_fn_schema.egg-info/PKG-INFO +56 -0
- matrix_fn_schema-0.1.0/src/matrix_fn_schema.egg-info/SOURCES.txt +11 -0
- matrix_fn_schema-0.1.0/src/matrix_fn_schema.egg-info/dependency_links.txt +1 -0
- matrix_fn_schema-0.1.0/src/matrix_fn_schema.egg-info/requires.txt +4 -0
- matrix_fn_schema-0.1.0/src/matrix_fn_schema.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: matrix-fn-schema
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python function signatures to OpenAI-compatible JSON Schema
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Keywords: openai,json-schema,function-calling,tools
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Requires-Dist: docstring-parser>=0.16
|
|
17
|
+
Provides-Extra: pydantic
|
|
18
|
+
Requires-Dist: pydantic>=2; extra == "pydantic"
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# matrixschema
|
|
22
|
+
|
|
23
|
+
Convert Python function signatures (type annotations + docstrings) into OpenAI-compatible JSON Schema (tool call format).
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from src.matrixschema import build_json_schema
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_weather(city: str, units: Literal["metric", "imperial"] = "metric") -> str:
|
|
30
|
+
"""Get the current weather for a city."""
|
|
31
|
+
...
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
schema = build_json_schema(get_weather)
|
|
35
|
+
# {
|
|
36
|
+
# "type": "function",
|
|
37
|
+
# "name": "get_weather",
|
|
38
|
+
# "description": "Get the current weather for a city.",
|
|
39
|
+
# "strict": True,
|
|
40
|
+
# "parameters": {
|
|
41
|
+
# "type": "object",
|
|
42
|
+
# "properties": {
|
|
43
|
+
# "city": {"type": "string"},
|
|
44
|
+
# "units": {"anyOf": [{"enum": ["metric", "imperial"]}, {"type": "null"}]}
|
|
45
|
+
# },
|
|
46
|
+
# "additionalProperties": False,
|
|
47
|
+
# "required": ["city", "units"]
|
|
48
|
+
# }
|
|
49
|
+
# }
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Supports: `int`, `float`, `str`, `bool`, `None`, `Optional[X]`, `Union[...]`, `Literal[...]`, `list[X]`, `tuple[X, ...]`, `dict[K, V]`, nested `pydantic.BaseModel`.
|
|
53
|
+
|
|
54
|
+
Requires Python 3.10+.
|
|
55
|
+
|
|
56
|
+
Written with love by dotmatrix.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# matrixschema
|
|
2
|
+
|
|
3
|
+
Convert Python function signatures (type annotations + docstrings) into OpenAI-compatible JSON Schema (tool call format).
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
from src.matrixschema import build_json_schema
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_weather(city: str, units: Literal["metric", "imperial"] = "metric") -> str:
|
|
10
|
+
"""Get the current weather for a city."""
|
|
11
|
+
...
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
schema = build_json_schema(get_weather)
|
|
15
|
+
# {
|
|
16
|
+
# "type": "function",
|
|
17
|
+
# "name": "get_weather",
|
|
18
|
+
# "description": "Get the current weather for a city.",
|
|
19
|
+
# "strict": True,
|
|
20
|
+
# "parameters": {
|
|
21
|
+
# "type": "object",
|
|
22
|
+
# "properties": {
|
|
23
|
+
# "city": {"type": "string"},
|
|
24
|
+
# "units": {"anyOf": [{"enum": ["metric", "imperial"]}, {"type": "null"}]}
|
|
25
|
+
# },
|
|
26
|
+
# "additionalProperties": False,
|
|
27
|
+
# "required": ["city", "units"]
|
|
28
|
+
# }
|
|
29
|
+
# }
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Supports: `int`, `float`, `str`, `bool`, `None`, `Optional[X]`, `Union[...]`, `Literal[...]`, `list[X]`, `tuple[X, ...]`, `dict[K, V]`, nested `pydantic.BaseModel`.
|
|
33
|
+
|
|
34
|
+
Requires Python 3.10+.
|
|
35
|
+
|
|
36
|
+
Written with love by dotmatrix.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=75"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "matrix-fn-schema"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Python function signatures to OpenAI-compatible JSON Schema"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
keywords = ["openai", "json-schema", "function-calling", "tools"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"Programming Language :: Python :: 3.10",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"docstring-parser>=0.16",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.optional-dependencies]
|
|
26
|
+
pydantic = ["pydantic>=2"]
|
|
27
|
+
|
|
28
|
+
[tool.setuptools.dynamic]
|
|
29
|
+
version = {attr = "matrix_fn_schema.__version__"}
|
|
30
|
+
|
|
31
|
+
[tool.setuptools.packages.find]
|
|
32
|
+
where = ["src"]
|
|
33
|
+
include = ["matrix_fn_schema*"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
from typing import Any, Callable, Union, Awaitable
|
|
2
|
+
|
|
3
|
+
from docstring_parser import parse as _parse_docstring
|
|
4
|
+
from typing import Literal, get_args, get_origin, get_type_hints
|
|
5
|
+
import sys
|
|
6
|
+
import inspect
|
|
7
|
+
|
|
8
|
+
AsyncOrSyncFunction = Union[Callable[..., object], Callable[..., Awaitable[object]]]
|
|
9
|
+
|
|
10
|
+
_PRIMITIVES: dict[type, str] = {
|
|
11
|
+
int: 'integer',
|
|
12
|
+
float: 'number',
|
|
13
|
+
str: 'string',
|
|
14
|
+
bool: 'boolean',
|
|
15
|
+
type(None): 'null',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _parse_param_docs(fn: AsyncOrSyncFunction) -> dict[str, str]:
|
|
20
|
+
return {
|
|
21
|
+
p.arg_name: p.description or ''
|
|
22
|
+
for p in _parse_docstring(inspect.getdoc(fn) or '').params
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _get_union_args(annotation: Any) -> tuple[Any, ...] | None:
|
|
27
|
+
"""
|
|
28
|
+
Returns Union/Optional/X|Y arguments, or None if not a Union.
|
|
29
|
+
Handles typing.Union and types.UnionType (Python 3.10+ pipe syntax).
|
|
30
|
+
"""
|
|
31
|
+
if get_origin(annotation) is Union:
|
|
32
|
+
return get_args(annotation)
|
|
33
|
+
if sys.version_info >= (3, 10):
|
|
34
|
+
import types
|
|
35
|
+
if type(annotation) is types.UnionType:
|
|
36
|
+
return get_args(annotation)
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _annotation_to_schema(annotation: Any) -> dict[str, Any]:
|
|
41
|
+
# empty / Any - no constraints
|
|
42
|
+
if annotation is inspect.Parameter.empty or annotation is Any:
|
|
43
|
+
return {}
|
|
44
|
+
|
|
45
|
+
if annotation is type(None):
|
|
46
|
+
return {'type': 'null'}
|
|
47
|
+
|
|
48
|
+
if annotation in _PRIMITIVES:
|
|
49
|
+
return {'type': _PRIMITIVES[annotation]}
|
|
50
|
+
|
|
51
|
+
# Union / Optional / X | Y
|
|
52
|
+
union_args = _get_union_args(annotation)
|
|
53
|
+
if union_args is not None:
|
|
54
|
+
null_schema = {'type': 'null'}
|
|
55
|
+
arg_schemas = [_annotation_to_schema(a) for a in union_args]
|
|
56
|
+
non_null = [s for s in arg_schemas if s != null_schema]
|
|
57
|
+
|
|
58
|
+
if len(arg_schemas) == 2 and null_schema in arg_schemas:
|
|
59
|
+
# Optional[X] / X | None - keep flat structure
|
|
60
|
+
return {'anyOf': [non_null[0], null_schema]}
|
|
61
|
+
return {'anyOf': arg_schemas}
|
|
62
|
+
|
|
63
|
+
origin = get_origin(annotation)
|
|
64
|
+
args = get_args(annotation)
|
|
65
|
+
|
|
66
|
+
# Literal["a", "b"]
|
|
67
|
+
if origin is Literal:
|
|
68
|
+
return {'enum': list(args)}
|
|
69
|
+
|
|
70
|
+
# list[X]
|
|
71
|
+
if origin is list:
|
|
72
|
+
schema: dict[str, Any] = {'type': 'array'}
|
|
73
|
+
if args:
|
|
74
|
+
schema['items'] = _annotation_to_schema(args[0])
|
|
75
|
+
return schema
|
|
76
|
+
|
|
77
|
+
# tuple[X, Y] / tuple[X, ...]
|
|
78
|
+
if origin is tuple:
|
|
79
|
+
if not args:
|
|
80
|
+
return {'type': 'array'}
|
|
81
|
+
if len(args) == 2 and args[1] is Ellipsis:
|
|
82
|
+
# tuple[int, ...] - variable length
|
|
83
|
+
return {'type': 'array', 'items': _annotation_to_schema(args[0])}
|
|
84
|
+
# tuple[int, str, float] - fixed structure
|
|
85
|
+
return {
|
|
86
|
+
'type': 'array',
|
|
87
|
+
'prefixItems': [_annotation_to_schema(a) for a in args],
|
|
88
|
+
'minItems': len(args),
|
|
89
|
+
'maxItems': len(args),
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# dict[K, V]
|
|
93
|
+
if origin is dict:
|
|
94
|
+
schema = {'type': 'object'}
|
|
95
|
+
if len(args) == 2:
|
|
96
|
+
val_schema = _annotation_to_schema(args[1])
|
|
97
|
+
if val_schema:
|
|
98
|
+
schema['additionalProperties'] = val_schema
|
|
99
|
+
return schema
|
|
100
|
+
|
|
101
|
+
# Pydantic BaseModel - nested schema
|
|
102
|
+
try:
|
|
103
|
+
from pydantic import BaseModel
|
|
104
|
+
if isinstance(annotation, type) and issubclass(annotation, BaseModel):
|
|
105
|
+
return annotation.model_json_schema()
|
|
106
|
+
except ImportError:
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
return {}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _is_optional_param(annotation: Any, default: Any) -> bool:
|
|
113
|
+
if default is not inspect.Parameter.empty:
|
|
114
|
+
return True
|
|
115
|
+
origin = get_origin(annotation)
|
|
116
|
+
return get_origin(origin) is Union and type(None) in get_args(annotation)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _make_strict_schema(base: dict[str, Any]) -> dict[str, Any]:
|
|
120
|
+
"""
|
|
121
|
+
In strict mode, a parameter with a default must accept null
|
|
122
|
+
(the LLM will pass null instead of omitting the argument).
|
|
123
|
+
If the schema already has anyOf with null, do not duplicate.
|
|
124
|
+
"""
|
|
125
|
+
null_schema = {'type': 'null'}
|
|
126
|
+
if not base:
|
|
127
|
+
return null_schema
|
|
128
|
+
# already nullable
|
|
129
|
+
if 'anyOf' in base and null_schema in base['anyOf']:
|
|
130
|
+
return base
|
|
131
|
+
return {'anyOf': [base, null_schema]}
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def build_json_schema(fn: AsyncOrSyncFunction) -> dict[str, Any]:
|
|
135
|
+
sig = inspect.signature(fn)
|
|
136
|
+
hints = get_type_hints(fn)
|
|
137
|
+
param_docs = _parse_param_docs(fn)
|
|
138
|
+
|
|
139
|
+
properties: dict[str, Any] = {}
|
|
140
|
+
required: list[str] = [] # in strict mode — all parameters
|
|
141
|
+
|
|
142
|
+
for name, param in sig.parameters.items():
|
|
143
|
+
if name in ('self', 'cls'):
|
|
144
|
+
continue
|
|
145
|
+
|
|
146
|
+
annotation = hints.get(name, inspect.Parameter.empty)
|
|
147
|
+
has_default = param.default is not inspect.Parameter.empty
|
|
148
|
+
base_schema = _annotation_to_schema(annotation)
|
|
149
|
+
|
|
150
|
+
# optional - anyOf [type, null] so the LLM can explicitly pass null
|
|
151
|
+
if has_default:
|
|
152
|
+
prop = _make_strict_schema(base_schema)
|
|
153
|
+
else:
|
|
154
|
+
prop = base_schema
|
|
155
|
+
|
|
156
|
+
if description := param_docs.get(name):
|
|
157
|
+
prop['description'] = description
|
|
158
|
+
|
|
159
|
+
properties[name] = prop
|
|
160
|
+
required.append(name) # always
|
|
161
|
+
|
|
162
|
+
description = (inspect.getdoc(fn) or '').replace('\n', ' ').strip()
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
'type': 'function',
|
|
166
|
+
'name': fn.__name__,
|
|
167
|
+
'description': description,
|
|
168
|
+
'strict': True,
|
|
169
|
+
'parameters': {
|
|
170
|
+
'type': 'object',
|
|
171
|
+
'properties': properties,
|
|
172
|
+
'additionalProperties': False,
|
|
173
|
+
'required': required,
|
|
174
|
+
},
|
|
175
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: matrix-fn-schema
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python function signatures to OpenAI-compatible JSON Schema
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Keywords: openai,json-schema,function-calling,tools
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Requires-Dist: docstring-parser>=0.16
|
|
17
|
+
Provides-Extra: pydantic
|
|
18
|
+
Requires-Dist: pydantic>=2; extra == "pydantic"
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# matrixschema
|
|
22
|
+
|
|
23
|
+
Convert Python function signatures (type annotations + docstrings) into OpenAI-compatible JSON Schema (tool call format).
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from src.matrixschema import build_json_schema
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_weather(city: str, units: Literal["metric", "imperial"] = "metric") -> str:
|
|
30
|
+
"""Get the current weather for a city."""
|
|
31
|
+
...
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
schema = build_json_schema(get_weather)
|
|
35
|
+
# {
|
|
36
|
+
# "type": "function",
|
|
37
|
+
# "name": "get_weather",
|
|
38
|
+
# "description": "Get the current weather for a city.",
|
|
39
|
+
# "strict": True,
|
|
40
|
+
# "parameters": {
|
|
41
|
+
# "type": "object",
|
|
42
|
+
# "properties": {
|
|
43
|
+
# "city": {"type": "string"},
|
|
44
|
+
# "units": {"anyOf": [{"enum": ["metric", "imperial"]}, {"type": "null"}]}
|
|
45
|
+
# },
|
|
46
|
+
# "additionalProperties": False,
|
|
47
|
+
# "required": ["city", "units"]
|
|
48
|
+
# }
|
|
49
|
+
# }
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Supports: `int`, `float`, `str`, `bool`, `None`, `Optional[X]`, `Union[...]`, `Literal[...]`, `list[X]`, `tuple[X, ...]`, `dict[K, V]`, nested `pydantic.BaseModel`.
|
|
53
|
+
|
|
54
|
+
Requires Python 3.10+.
|
|
55
|
+
|
|
56
|
+
Written with love by dotmatrix.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/matrix_fn_schema/__init__.py
|
|
5
|
+
src/matrix_fn_schema/py.typed
|
|
6
|
+
src/matrix_fn_schema/schema.py
|
|
7
|
+
src/matrix_fn_schema.egg-info/PKG-INFO
|
|
8
|
+
src/matrix_fn_schema.egg-info/SOURCES.txt
|
|
9
|
+
src/matrix_fn_schema.egg-info/dependency_links.txt
|
|
10
|
+
src/matrix_fn_schema.egg-info/requires.txt
|
|
11
|
+
src/matrix_fn_schema.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
matrix_fn_schema
|