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.
@@ -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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,5 @@
1
+ __version__ = "0.1.0"
2
+
3
+ from matrix_fn_schema.schema import build_json_schema
4
+
5
+ __all__ = ["build_json_schema"]
@@ -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,4 @@
1
+ docstring-parser>=0.16
2
+
3
+ [pydantic]
4
+ pydantic>=2
@@ -0,0 +1 @@
1
+ matrix_fn_schema