schemez 1.2.0__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/convert.py +1 -1
- schemez/functionschema.py +11 -11
- schemez/helpers.py +9 -5
- schemez/log.py +17 -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 +2 -2
- schemez-1.2.2.dist-info/METADATA +340 -0
- schemez-1.2.2.dist-info/RECORD +25 -0
- {schemez-1.2.0.dist-info → schemez-1.2.2.dist-info}/WHEEL +1 -1
- schemez-1.2.2.dist-info/licenses/LICENSE +22 -0
- schemez-1.2.0.dist-info/METADATA +0 -86
- schemez-1.2.0.dist-info/RECORD +0 -19
schemez/convert.py
CHANGED
@@ -64,7 +64,7 @@ def get_function_model(func: AnyCallable, *, name: str | None = None) -> type[Sc
|
|
64
64
|
|
65
65
|
for param_name, param in sig.parameters.items():
|
66
66
|
# Skip self/cls for methods
|
67
|
-
if param_name in
|
67
|
+
if param_name in {"self", "cls"}:
|
68
68
|
continue
|
69
69
|
|
70
70
|
type_hint = hints.get(param_name, Any)
|
schemez/functionschema.py
CHANGED
@@ -37,7 +37,7 @@ if typing.TYPE_CHECKING:
|
|
37
37
|
logger = logging.getLogger(__name__)
|
38
38
|
|
39
39
|
|
40
|
-
class FunctionType(
|
40
|
+
class FunctionType(enum.StrEnum):
|
41
41
|
"""Enum representing different function types."""
|
42
42
|
|
43
43
|
SYNC = "sync"
|
@@ -393,7 +393,7 @@ def _is_optional_type(typ: type) -> TypeGuard[type]:
|
|
393
393
|
True if the type is Optional, False otherwise
|
394
394
|
"""
|
395
395
|
origin = typing.get_origin(typ)
|
396
|
-
if origin not in
|
396
|
+
if origin not in {typing.Union, types.UnionType}: # pyright: ignore
|
397
397
|
return False
|
398
398
|
args = typing.get_args(typ)
|
399
399
|
# Check if any of the union members is None or NoneType
|
@@ -448,7 +448,7 @@ def _resolve_type_annotation(
|
|
448
448
|
args = typing.get_args(typ)
|
449
449
|
|
450
450
|
# Handle Union types (including Optional)
|
451
|
-
if origin in
|
451
|
+
if origin in {typing.Union, types.UnionType}: # pyright: ignore
|
452
452
|
# For Optional (union with None), filter out None type
|
453
453
|
non_none_types = [t for t in args if t is not type(None)]
|
454
454
|
if non_none_types:
|
@@ -495,7 +495,7 @@ def _resolve_type_annotation(
|
|
495
495
|
schema["required"] = required
|
496
496
|
# Handle mappings - updated check
|
497
497
|
elif (
|
498
|
-
origin in
|
498
|
+
origin in {dict, typing.Dict} # noqa: UP006
|
499
499
|
or (origin is not None and isinstance(origin, type) and issubclass(origin, dict))
|
500
500
|
):
|
501
501
|
schema["type"] = "object"
|
@@ -503,14 +503,14 @@ def _resolve_type_annotation(
|
|
503
503
|
schema["additionalProperties"] = True
|
504
504
|
|
505
505
|
# Handle sequences
|
506
|
-
elif origin in
|
506
|
+
elif origin in {
|
507
507
|
list,
|
508
508
|
set,
|
509
509
|
tuple,
|
510
510
|
frozenset,
|
511
511
|
typing.List, # noqa: UP006 # pyright: ignore
|
512
512
|
typing.Set, # noqa: UP006 # pyright: ignore
|
513
|
-
|
513
|
+
} or (
|
514
514
|
origin is not None
|
515
515
|
and origin.__module__ == "collections.abc"
|
516
516
|
and origin.__name__ in {"Sequence", "MutableSequence", "Collection"}
|
@@ -534,11 +534,11 @@ def _resolve_type_annotation(
|
|
534
534
|
schema["enum"] = [e.value for e in typ]
|
535
535
|
|
536
536
|
# Basic types
|
537
|
-
elif typ in
|
537
|
+
elif typ in {str, Path, UUID, re.Pattern}:
|
538
538
|
schema["type"] = "string"
|
539
539
|
elif typ is int:
|
540
540
|
schema["type"] = "integer"
|
541
|
-
elif typ in
|
541
|
+
elif typ in {float, decimal.Decimal}:
|
542
542
|
schema["type"] = "number"
|
543
543
|
elif typ is bool:
|
544
544
|
schema["type"] = "boolean"
|
@@ -696,10 +696,10 @@ def create_schema(
|
|
696
696
|
# Skip the first parameter for bound methods
|
697
697
|
if skip_first and i == 0:
|
698
698
|
continue
|
699
|
-
if param.kind in
|
699
|
+
if param.kind in {
|
700
700
|
inspect.Parameter.VAR_POSITIONAL,
|
701
701
|
inspect.Parameter.VAR_KEYWORD,
|
702
|
-
|
702
|
+
}:
|
703
703
|
continue
|
704
704
|
|
705
705
|
param_doc = next(
|
@@ -726,7 +726,7 @@ def create_schema(
|
|
726
726
|
function_type = _determine_function_type(func)
|
727
727
|
return_hint = hints.get("return", Any)
|
728
728
|
|
729
|
-
if function_type in
|
729
|
+
if function_type in {FunctionType.SYNC_GENERATOR, FunctionType.ASYNC_GENERATOR}:
|
730
730
|
element_type = next(
|
731
731
|
(t for t in typing.get_args(return_hint) if t is not type(None)),
|
732
732
|
Any,
|
schemez/helpers.py
CHANGED
@@ -214,16 +214,16 @@ async def _detect_command(command: str, *, test_flag: str = "--version") -> list
|
|
214
214
|
|
215
215
|
|
216
216
|
async def model_to_python_code(
|
217
|
-
model: type[BaseModel],
|
217
|
+
model: type[BaseModel] | dict[str, Any],
|
218
218
|
*,
|
219
219
|
class_name: str | None = None,
|
220
220
|
target_python_version: PythonVersion | None = None,
|
221
221
|
model_type: str = "pydantic.BaseModel",
|
222
222
|
) -> str:
|
223
|
-
"""Convert a BaseModel to Python code asynchronously.
|
223
|
+
"""Convert a BaseModel or schema dict to Python code asynchronously.
|
224
224
|
|
225
225
|
Args:
|
226
|
-
model: The BaseModel class to convert
|
226
|
+
model: The BaseModel class or schema dictionary to convert
|
227
227
|
class_name: Optional custom class name for the generated code
|
228
228
|
target_python_version: Target Python version for code generation.
|
229
229
|
Defaults to current system Python version.
|
@@ -238,8 +238,12 @@ async def model_to_python_code(
|
|
238
238
|
"""
|
239
239
|
working_prefix = await _detect_command("datamodel-codegen")
|
240
240
|
|
241
|
-
|
242
|
-
|
241
|
+
if isinstance(model, dict):
|
242
|
+
schema = model
|
243
|
+
name = class_name or "GeneratedModel"
|
244
|
+
else:
|
245
|
+
schema = model.model_json_schema()
|
246
|
+
name = class_name or model.__name__
|
243
247
|
py = target_python_version or f"{sys.version_info.major}.{sys.version_info.minor}"
|
244
248
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
|
245
249
|
# Use pydantic_core.to_json for proper schema serialization
|
schemez/log.py
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
"""Logging configuration for schemez."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
import logging
|
6
|
+
|
7
|
+
|
8
|
+
def get_logger(name: str) -> logging.Logger:
|
9
|
+
"""Get a logger for the given name.
|
10
|
+
|
11
|
+
Args:
|
12
|
+
name: The name of the logger, will be prefixed with 'llmling_agent.'
|
13
|
+
|
14
|
+
Returns:
|
15
|
+
A logger instance
|
16
|
+
"""
|
17
|
+
return logging.getLogger(f"schemez.{name}")
|
@@ -0,0 +1,8 @@
|
|
1
|
+
"""Tool executor module for HTTP tool generation and execution."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from schemez.tool_executor.executor import HttpToolExecutor
|
6
|
+
from schemez.tool_executor.types import ToolHandler
|
7
|
+
|
8
|
+
__all__ = ["HttpToolExecutor", "ToolHandler"]
|
@@ -0,0 +1,322 @@
|
|
1
|
+
"""HTTP tool executor for managing tool generation and execution."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
import asyncio
|
6
|
+
import logging
|
7
|
+
from pathlib import Path
|
8
|
+
import time
|
9
|
+
from typing import TYPE_CHECKING, Any
|
10
|
+
|
11
|
+
from pydantic import BaseModel
|
12
|
+
from pydantic_core import from_json
|
13
|
+
from upath import UPath
|
14
|
+
|
15
|
+
from schemez.functionschema import FunctionSchema
|
16
|
+
from schemez.tool_executor.helpers import clean_generated_code, generate_input_model
|
17
|
+
|
18
|
+
|
19
|
+
if TYPE_CHECKING:
|
20
|
+
from collections.abc import Callable, Sequence
|
21
|
+
|
22
|
+
from fastapi import FastAPI
|
23
|
+
|
24
|
+
from schemez.tool_executor.types import ToolHandler
|
25
|
+
|
26
|
+
|
27
|
+
logger = logging.getLogger(__name__)
|
28
|
+
|
29
|
+
|
30
|
+
class HttpToolExecutor:
|
31
|
+
"""Manages HTTP tool generation and execution."""
|
32
|
+
|
33
|
+
def __init__(
|
34
|
+
self,
|
35
|
+
schemas: Sequence[dict[str, Any] | Path],
|
36
|
+
handler: ToolHandler,
|
37
|
+
base_url: str = "http://localhost:8000",
|
38
|
+
):
|
39
|
+
"""Initialize the tool executor.
|
40
|
+
|
41
|
+
Args:
|
42
|
+
schemas: List of tool schema dictionaries or file paths
|
43
|
+
handler: User-provided tool handler function
|
44
|
+
base_url: Base URL for the tool server
|
45
|
+
"""
|
46
|
+
self.schemas = schemas
|
47
|
+
self.handler = handler
|
48
|
+
self.base_url = base_url
|
49
|
+
|
50
|
+
# Cached artifacts
|
51
|
+
self._tool_mappings: dict[str, str] | None = None
|
52
|
+
self._tools_code: str | None = None
|
53
|
+
self._server_app: FastAPI | None = None
|
54
|
+
self._tool_functions: dict[str, Callable] | None = None
|
55
|
+
|
56
|
+
async def _load_schemas(self) -> list[dict]:
|
57
|
+
"""Load and normalize schemas from various sources."""
|
58
|
+
loaded_schemas = []
|
59
|
+
|
60
|
+
for schema in self.schemas:
|
61
|
+
match schema:
|
62
|
+
case dict():
|
63
|
+
loaded_schemas.append(schema)
|
64
|
+
case str() | Path():
|
65
|
+
text = UPath(schema).read_text("utf-8")
|
66
|
+
loaded_schemas.append(from_json(text))
|
67
|
+
case _:
|
68
|
+
msg = f"Invalid schema type: {type(schema)}"
|
69
|
+
raise TypeError(msg)
|
70
|
+
|
71
|
+
return loaded_schemas
|
72
|
+
|
73
|
+
async def _get_tool_mappings(self) -> dict[str, str]:
|
74
|
+
"""Get tool name to input class mappings."""
|
75
|
+
if self._tool_mappings is None:
|
76
|
+
self._tool_mappings = {}
|
77
|
+
schemas = await self._load_schemas()
|
78
|
+
|
79
|
+
for schema_dict in schemas:
|
80
|
+
function_schema = FunctionSchema.from_dict(schema_dict)
|
81
|
+
name = "".join(word.title() for word in function_schema.name.split("_"))
|
82
|
+
input_class_name = f"{name}Input"
|
83
|
+
self._tool_mappings[function_schema.name] = input_class_name
|
84
|
+
|
85
|
+
return self._tool_mappings
|
86
|
+
|
87
|
+
async def _generate_http_wrapper(
|
88
|
+
self, schema_dict: dict, input_class_name: str
|
89
|
+
) -> str:
|
90
|
+
"""Generate HTTP wrapper function."""
|
91
|
+
name = schema_dict["name"]
|
92
|
+
description = schema_dict.get("description", "")
|
93
|
+
|
94
|
+
return f'''
|
95
|
+
async def {name}(input: {input_class_name}) -> str:
|
96
|
+
"""{description}
|
97
|
+
|
98
|
+
Args:
|
99
|
+
input: Function parameters
|
100
|
+
|
101
|
+
Returns:
|
102
|
+
String response from the tool server
|
103
|
+
"""
|
104
|
+
import httpx
|
105
|
+
async with httpx.AsyncClient() as client:
|
106
|
+
response = await client.post(
|
107
|
+
"{self.base_url}/tools/{name}",
|
108
|
+
json=input.model_dump(),
|
109
|
+
timeout=30.0
|
110
|
+
)
|
111
|
+
response.raise_for_status()
|
112
|
+
return response.text
|
113
|
+
'''
|
114
|
+
|
115
|
+
async def generate_tools_code(self) -> str:
|
116
|
+
"""Generate HTTP wrapper tools as Python code."""
|
117
|
+
if self._tools_code is not None:
|
118
|
+
return self._tools_code
|
119
|
+
|
120
|
+
start_time = time.time()
|
121
|
+
logger.info("Starting tools code generation")
|
122
|
+
|
123
|
+
schemas = await self._load_schemas()
|
124
|
+
code_parts: list[str] = []
|
125
|
+
await self._get_tool_mappings()
|
126
|
+
|
127
|
+
# Module header
|
128
|
+
header = '''"""Generated HTTP wrapper tools."""
|
129
|
+
|
130
|
+
from __future__ import annotations
|
131
|
+
|
132
|
+
from pydantic import BaseModel, Field
|
133
|
+
from typing import Literal, List, Any
|
134
|
+
from datetime import datetime
|
135
|
+
|
136
|
+
'''
|
137
|
+
code_parts.append(header)
|
138
|
+
|
139
|
+
# Generate models and wrappers for each tool
|
140
|
+
all_exports = []
|
141
|
+
for schema_dict in schemas:
|
142
|
+
function_schema = FunctionSchema.from_dict(schema_dict)
|
143
|
+
|
144
|
+
schema_data = {
|
145
|
+
"name": function_schema.name,
|
146
|
+
"description": function_schema.description,
|
147
|
+
"parameters": function_schema.parameters,
|
148
|
+
}
|
149
|
+
|
150
|
+
# Generate input model (strip future imports from generated code)
|
151
|
+
input_code, input_class_name = await generate_input_model(schema_data)
|
152
|
+
# Remove future imports and datamodel-codegen header from individual models
|
153
|
+
cleaned_input_code = clean_generated_code(input_code)
|
154
|
+
code_parts.append(cleaned_input_code)
|
155
|
+
wrapper_code = await self._generate_http_wrapper(
|
156
|
+
schema_data, input_class_name
|
157
|
+
)
|
158
|
+
code_parts.append(wrapper_code)
|
159
|
+
|
160
|
+
all_exports.extend([input_class_name, function_schema.name])
|
161
|
+
|
162
|
+
# Add exports
|
163
|
+
code_parts.append(f"\n__all__ = {all_exports}\n")
|
164
|
+
|
165
|
+
self._tools_code = "\n".join(code_parts)
|
166
|
+
elapsed = time.time() - start_time
|
167
|
+
logger.info(f"Tools code generation completed in {elapsed:.2f}s") # noqa: G004
|
168
|
+
return self._tools_code
|
169
|
+
|
170
|
+
async def generate_server_app(self) -> FastAPI:
|
171
|
+
"""Create configured FastAPI server."""
|
172
|
+
from fastapi import FastAPI, HTTPException
|
173
|
+
|
174
|
+
if self._server_app is not None:
|
175
|
+
return self._server_app
|
176
|
+
|
177
|
+
tool_mappings = await self._get_tool_mappings()
|
178
|
+
app = FastAPI(title="Tool Server", version="1.0.0")
|
179
|
+
|
180
|
+
@app.post("/tools/{tool_name}")
|
181
|
+
async def handle_tool_call(tool_name: str, input_data: dict) -> str:
|
182
|
+
"""Generic endpoint that routes all tool calls to user handler."""
|
183
|
+
# Validate tool exists
|
184
|
+
if tool_name not in tool_mappings:
|
185
|
+
tools = list(tool_mappings.keys())
|
186
|
+
detail = f"Tool '{tool_name}' not found. Available: {tools}"
|
187
|
+
raise HTTPException(status_code=404, detail=detail)
|
188
|
+
|
189
|
+
# Create a simple BaseModel for validation
|
190
|
+
class DynamicInput(BaseModel):
|
191
|
+
pass
|
192
|
+
|
193
|
+
# Add fields dynamically (basic validation only)
|
194
|
+
dynamic_input = DynamicInput(**input_data)
|
195
|
+
|
196
|
+
# Call user's handler
|
197
|
+
try:
|
198
|
+
return await self.handler(tool_name, dynamic_input)
|
199
|
+
except Exception as e:
|
200
|
+
msg = f"Tool execution failed: {e}"
|
201
|
+
raise HTTPException(status_code=500, detail=msg) from e
|
202
|
+
|
203
|
+
self._server_app = app
|
204
|
+
return app
|
205
|
+
|
206
|
+
async def get_tool_functions(self) -> dict[str, Callable]:
|
207
|
+
"""Get ready-to-use tool functions."""
|
208
|
+
if self._tool_functions is not None:
|
209
|
+
return self._tool_functions
|
210
|
+
|
211
|
+
start_time = time.time()
|
212
|
+
logger.info("Starting tool functions generation")
|
213
|
+
|
214
|
+
# Generate and execute the tools code
|
215
|
+
tools_code = await self.generate_tools_code()
|
216
|
+
logger.debug("Generated %s characters of code", len(tools_code))
|
217
|
+
|
218
|
+
# Create namespace and execute
|
219
|
+
namespace = {
|
220
|
+
"BaseModel": BaseModel,
|
221
|
+
"Field": BaseModel.model_fields_set,
|
222
|
+
"Literal": Any,
|
223
|
+
"List": list,
|
224
|
+
"datetime": __import__("datetime").datetime,
|
225
|
+
}
|
226
|
+
|
227
|
+
logger.debug("Executing generated tools code...")
|
228
|
+
exec_start = time.time()
|
229
|
+
exec(tools_code, namespace)
|
230
|
+
exec_elapsed = time.time() - exec_start
|
231
|
+
logger.debug("Code execution completed in %.2fs", exec_elapsed)
|
232
|
+
|
233
|
+
# Extract tool functions
|
234
|
+
tool_mappings = await self._get_tool_mappings()
|
235
|
+
self._tool_functions = {
|
236
|
+
tool_name: namespace[tool_name]
|
237
|
+
for tool_name in tool_mappings
|
238
|
+
if tool_name in namespace
|
239
|
+
}
|
240
|
+
|
241
|
+
elapsed = time.time() - start_time
|
242
|
+
logger.info(f"Tool functions generation completed in {elapsed:.2f}s") # noqa: G004
|
243
|
+
return self._tool_functions
|
244
|
+
|
245
|
+
async def start_server(
|
246
|
+
self, host: str = "0.0.0.0", port: int = 8000, background: bool = False
|
247
|
+
) -> None | asyncio.Task:
|
248
|
+
"""Start the FastAPI server.
|
249
|
+
|
250
|
+
Args:
|
251
|
+
host: Host to bind to
|
252
|
+
port: Port to bind to
|
253
|
+
background: If True, run server in background task
|
254
|
+
"""
|
255
|
+
import uvicorn
|
256
|
+
|
257
|
+
app = await self.generate_server_app()
|
258
|
+
|
259
|
+
if background:
|
260
|
+
config = uvicorn.Config(app, host=host, port=port)
|
261
|
+
server = uvicorn.Server(config)
|
262
|
+
return asyncio.create_task(server.serve())
|
263
|
+
|
264
|
+
uvicorn.run(app, host=host, port=port)
|
265
|
+
return None
|
266
|
+
|
267
|
+
async def save_to_files(self, output_dir: Path) -> dict[str, Path]:
|
268
|
+
"""Save generated code to files.
|
269
|
+
|
270
|
+
Args:
|
271
|
+
output_dir: Directory to save files to
|
272
|
+
|
273
|
+
Returns:
|
274
|
+
Dictionary mapping file types to paths
|
275
|
+
"""
|
276
|
+
output_dir = Path(output_dir)
|
277
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
278
|
+
|
279
|
+
saved_files = {}
|
280
|
+
|
281
|
+
# Save tools module
|
282
|
+
tools_code = await self.generate_tools_code()
|
283
|
+
tools_file = output_dir / "generated_tools.py"
|
284
|
+
tools_file.write_text(tools_code)
|
285
|
+
saved_files["tools"] = tools_file
|
286
|
+
|
287
|
+
# Save server code (as template/example)
|
288
|
+
server_template = f'''"""FastAPI server using HttpToolExecutor."""
|
289
|
+
|
290
|
+
import asyncio
|
291
|
+
from pathlib import Path
|
292
|
+
|
293
|
+
from schemez.tool_executor import HttpToolExecutor, ToolHandler
|
294
|
+
from pydantic import BaseModel
|
295
|
+
|
296
|
+
|
297
|
+
async def my_tool_handler(method_name: str, input_props: BaseModel) -> str:
|
298
|
+
"""Implement your tool logic here."""
|
299
|
+
match method_name:
|
300
|
+
case _:
|
301
|
+
return f"Mock result for {{method_name}}: {{input_props}}"
|
302
|
+
|
303
|
+
|
304
|
+
async def main():
|
305
|
+
"""Start the server with your handler."""
|
306
|
+
executor = HttpToolExecutor(
|
307
|
+
schemas=[], # Add your schema files/dicts here
|
308
|
+
handler=my_tool_handler,
|
309
|
+
base_url="{self.base_url}"
|
310
|
+
)
|
311
|
+
|
312
|
+
await executor.start_server()
|
313
|
+
|
314
|
+
|
315
|
+
if __name__ == "__main__":
|
316
|
+
asyncio.run(main())
|
317
|
+
'''
|
318
|
+
|
319
|
+
server_file = output_dir / "server_example.py"
|
320
|
+
server_file.write_text(server_template)
|
321
|
+
saved_files["server_example"] = server_file
|
322
|
+
return saved_files
|
@@ -0,0 +1,46 @@
|
|
1
|
+
"""Helper functions for the tool executor."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
import logging
|
6
|
+
import time
|
7
|
+
|
8
|
+
from schemez.helpers import model_to_python_code
|
9
|
+
|
10
|
+
|
11
|
+
logger = logging.getLogger(__name__)
|
12
|
+
|
13
|
+
|
14
|
+
async def generate_input_model(schema_dict: dict) -> tuple[str, str]:
|
15
|
+
"""Generate input model code from schema."""
|
16
|
+
start_time = time.time()
|
17
|
+
logger.debug("Generating input model for %s", schema_dict["name"])
|
18
|
+
|
19
|
+
words = [word.title() for word in schema_dict["name"].split("_")]
|
20
|
+
cls_name = f"{''.join(words)}Input"
|
21
|
+
code = await model_to_python_code(schema_dict["parameters"], class_name=cls_name)
|
22
|
+
elapsed = time.time() - start_time
|
23
|
+
logger.debug("Generated input model for %s in %.2fs", schema_dict["name"], elapsed)
|
24
|
+
return code, cls_name
|
25
|
+
|
26
|
+
|
27
|
+
def clean_generated_code(code: str) -> str:
|
28
|
+
"""Clean generated code by removing future imports and headers."""
|
29
|
+
lines = code.split("\n")
|
30
|
+
cleaned_lines = []
|
31
|
+
skip_until_class = True
|
32
|
+
|
33
|
+
for line in lines:
|
34
|
+
# Skip lines until we find a class or other meaningful content
|
35
|
+
if skip_until_class:
|
36
|
+
if line.strip().startswith("class ") or (
|
37
|
+
line.strip()
|
38
|
+
and not line.startswith("#")
|
39
|
+
and not line.startswith("from __future__")
|
40
|
+
):
|
41
|
+
skip_until_class = False
|
42
|
+
cleaned_lines.append(line)
|
43
|
+
continue
|
44
|
+
cleaned_lines.append(line)
|
45
|
+
|
46
|
+
return "\n".join(cleaned_lines)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
"""Type definitions for tool executor."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from typing import TYPE_CHECKING, Protocol
|
6
|
+
|
7
|
+
|
8
|
+
if TYPE_CHECKING:
|
9
|
+
from pydantic import BaseModel
|
10
|
+
|
11
|
+
|
12
|
+
class ToolHandler(Protocol):
|
13
|
+
"""Protocol for user-provided tool handler."""
|
14
|
+
|
15
|
+
async def __call__(self, method_name: str, input_props: BaseModel) -> str:
|
16
|
+
"""Process a tool call.
|
17
|
+
|
18
|
+
Args:
|
19
|
+
method_name: Name of the tool being called (e.g., "get_weather")
|
20
|
+
input_props: Validated input model instance
|
21
|
+
|
22
|
+
Returns:
|
23
|
+
String result from the tool execution
|
24
|
+
|
25
|
+
Raises:
|
26
|
+
Exception: If tool execution fails
|
27
|
+
"""
|
28
|
+
...
|
schemez/typedefs.py
CHANGED
@@ -163,7 +163,7 @@ def _convert_complex_property(
|
|
163
163
|
|
164
164
|
# Get first non-null type or default to string
|
165
165
|
first_type = next((t for t in types if t != "null"), "string")
|
166
|
-
if first_type not in
|
166
|
+
if first_type not in {"string", "number", "integer", "boolean"}:
|
167
167
|
first_type = "string"
|
168
168
|
|
169
169
|
return _create_simple_property(
|
@@ -193,7 +193,7 @@ def _convert_complex_property(
|
|
193
193
|
)
|
194
194
|
|
195
195
|
type_str = prop.get("type", "string")
|
196
|
-
if type_str not in
|
196
|
+
if type_str not in {"string", "number", "integer", "boolean"}:
|
197
197
|
type_str = "string"
|
198
198
|
|
199
199
|
return _create_simple_property(
|
@@ -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.
|
@@ -0,0 +1,25 @@
|
|
1
|
+
schemez/__init__.py,sha256=KkwJF8pfbzw_4cBxwXuUyXIExqdxS8iumvyAL-JUED4,1669
|
2
|
+
schemez/bind_kwargs.py,sha256=ChyArgNa5R8VdwSJmmrQItMH9Ld6hStWBISw-T1wyws,6228
|
3
|
+
schemez/code.py,sha256=usZLov9i5KpK1W2VJxngUzeetgrINtodiooG_AxN-y4,2072
|
4
|
+
schemez/convert.py,sha256=3sOxOgDaFzV7uiOUSM6_Sy0YlafIlZRSevs5y2vT1Kw,4403
|
5
|
+
schemez/create_type.py,sha256=wrdqdzXtfxZfsgp9IroldoYGTJs_Rdli8TiscqhV2bI,11647
|
6
|
+
schemez/docstrings.py,sha256=kmd660wcomXzKac0SSNYxPRNbVCUovrpmE9jwnVRS6c,4115
|
7
|
+
schemez/executable.py,sha256=YM4WcmRyJ9CpzKpNgS0A-Ri0Jd7mAzfHmoaXONI-mIs,7134
|
8
|
+
schemez/functionschema.py,sha256=kAV3W8_ory2vIlJELaSajQUgo8RcbEdu7ACkYpgWniI,26125
|
9
|
+
schemez/helpers.py,sha256=6PnZfbwiQPZGiuHScqkpT3kA2Wm4O3O79gsGA95l7ow,8888
|
10
|
+
schemez/log.py,sha256=i0SDbIfWmuC_nfJdQOYAdUYaR0TBk3Mhu-K3M-lnAM4,364
|
11
|
+
schemez/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
+
schemez/pydantic_types.py,sha256=8vgSl8i2z9n0fB-8AJj-D3TBByEWE5IxItBxQ0XwXFI,1640
|
13
|
+
schemez/schema.py,sha256=u6SDhYDtfCjgy2Aa-_MDLLNcUfEXbeye4T-W6s3AED8,9558
|
14
|
+
schemez/schema_generators.py,sha256=Gze7S7dQkTsl_1ckeHLXPxx4jQo7RB6hHQM-5fpAsrA,6973
|
15
|
+
schemez/schemadef/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
|
+
schemez/schemadef/schemadef.py,sha256=FtD7TOnYxiuYOIfadRHKkkbZn98mWFb0_lKfPsPR-hI,14393
|
17
|
+
schemez/tool_executor/__init__.py,sha256=7wLjhA1NGekTMsiIfWLAv6J7qYhWDlanH9DKU4v1c6c,263
|
18
|
+
schemez/tool_executor/executor.py,sha256=lQFnVO5J64nFNEsy82jRS-q52PaU3fWsnuQddaLOo6c,10410
|
19
|
+
schemez/tool_executor/helpers.py,sha256=jxBv1G_R8rQU3ACHGgfVEecqO0DSZQ6qUIHcn25Ucc4,1470
|
20
|
+
schemez/tool_executor/types.py,sha256=l2DxUIEHP9bjLnEaXZ6X428cSviicTDJsc3wfSNqKxg,675
|
21
|
+
schemez/typedefs.py,sha256=3OAUQ1nin9nlsOcTPAO5xrsOqVUfwsH_7_cexQYREus,6091
|
22
|
+
schemez-1.2.2.dist-info/licenses/LICENSE,sha256=AteGCH9r177TxxrOFEiOARrastASsf7yW6MQxlAHdwA,1078
|
23
|
+
schemez-1.2.2.dist-info/WHEEL,sha256=ZbtZh9LqsQoZs-WmwRO6z-tavdkb5LzNxvrOv2F_OXE,78
|
24
|
+
schemez-1.2.2.dist-info/METADATA,sha256=hIMu_x6gj7dGIFYuILQvUcMb_IvE10OPP_nLcXKWXSA,11616
|
25
|
+
schemez-1.2.2.dist-info/RECORD,,
|
@@ -0,0 +1,22 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2024, Philipp Temminghoff
|
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.
|
22
|
+
|
schemez-1.2.0.dist-info/METADATA
DELETED
@@ -1,86 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.3
|
2
|
-
Name: schemez
|
3
|
-
Version: 1.2.0
|
4
|
-
Summary: Pydantic shim for config stuff
|
5
|
-
Keywords:
|
6
|
-
Author: Philipp Temminghoff
|
7
|
-
Author-email: Philipp Temminghoff <philipptemminghoff@googlemail.com>
|
8
|
-
License: MIT License
|
9
|
-
|
10
|
-
Copyright (c) 2024, Philipp Temminghoff
|
11
|
-
|
12
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
13
|
-
of this software and associated documentation files (the "Software"), to deal
|
14
|
-
in the Software without restriction, including without limitation the rights
|
15
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
16
|
-
copies of the Software, and to permit persons to whom the Software is
|
17
|
-
furnished to do so, subject to the following conditions:
|
18
|
-
|
19
|
-
The above copyright notice and this permission notice shall be included in all
|
20
|
-
copies or substantial portions of the Software.
|
21
|
-
|
22
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
23
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
24
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
25
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
26
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
27
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
28
|
-
SOFTWARE.
|
29
|
-
|
30
|
-
Classifier: Development Status :: 4 - Beta
|
31
|
-
Classifier: Framework :: Pydantic
|
32
|
-
Classifier: Framework :: Pydantic :: 2
|
33
|
-
Classifier: Intended Audience :: Developers
|
34
|
-
Classifier: Operating System :: OS Independent
|
35
|
-
Classifier: Programming Language :: Python :: 3
|
36
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
37
|
-
Classifier: Programming Language :: Python :: 3.12
|
38
|
-
Classifier: Programming Language :: Python :: 3.13
|
39
|
-
Classifier: Programming Language :: Python :: 3.14
|
40
|
-
Classifier: Topic :: Documentation
|
41
|
-
Classifier: Topic :: Software Development
|
42
|
-
Classifier: Topic :: Utilities
|
43
|
-
Classifier: Typing :: Typed
|
44
|
-
Requires-Dist: docstring-parser>=0.17.0
|
45
|
-
Requires-Dist: griffe>=1.7.3
|
46
|
-
Requires-Dist: pydantic
|
47
|
-
Requires-Dist: universal-pathlib>=0.2.6
|
48
|
-
Requires-Dist: llmling-agent ; extra == 'ai'
|
49
|
-
Requires-Dist: anyenv>=0.4.14 ; extra == 'ai'
|
50
|
-
Requires-Dist: yamling ; extra == 'yaml'
|
51
|
-
Requires-Python: >=3.13
|
52
|
-
Project-URL: Code coverage, https://app.codecov.io/gh/phil65/schemez
|
53
|
-
Project-URL: Discussions, https://github.com/phil65/schemez/discussions
|
54
|
-
Project-URL: Documentation, https://phil65.github.io/schemez/
|
55
|
-
Project-URL: Issues, https://github.com/phil65/schemez/issues
|
56
|
-
Project-URL: Source, https://github.com/phil65/schemez
|
57
|
-
Provides-Extra: ai
|
58
|
-
Provides-Extra: yaml
|
59
|
-
Description-Content-Type: text/markdown
|
60
|
-
|
61
|
-
# Schemez
|
62
|
-
|
63
|
-
[](https://pypi.org/project/schemez/)
|
64
|
-
[](https://pypi.org/project/schemez/)
|
65
|
-
[](https://pypi.org/project/schemez/)
|
66
|
-
[](https://pypi.org/project/schemez/)
|
67
|
-
[](https://pypi.org/project/schemez/)
|
68
|
-
[](https://pypi.org/project/schemez/)
|
69
|
-
[](https://pypi.org/project/schemez/)
|
70
|
-
[](https://github.com/phil65/schemez/releases)
|
71
|
-
[](https://github.com/phil65/schemez/graphs/contributors)
|
72
|
-
[](https://github.com/phil65/schemez/discussions)
|
73
|
-
[](https://github.com/phil65/schemez/forks)
|
74
|
-
[](https://github.com/phil65/schemez/issues)
|
75
|
-
[](https://github.com/phil65/schemez/pulls)
|
76
|
-
[](https://github.com/phil65/schemez/watchers)
|
77
|
-
[](https://github.com/phil65/schemez/stars)
|
78
|
-
[](https://github.com/phil65/schemez)
|
79
|
-
[](https://github.com/phil65/schemez/commits)
|
80
|
-
[](https://github.com/phil65/schemez/releases)
|
81
|
-
[](https://github.com/phil65/schemez)
|
82
|
-
[](https://github.com/phil65/schemez)
|
83
|
-
[](https://codecov.io/gh/phil65/schemez/)
|
84
|
-
[](https://pyup.io/repos/github/phil65/schemez/)
|
85
|
-
|
86
|
-
[Read the documentation!](https://phil65.github.io/schemez/)
|
schemez-1.2.0.dist-info/RECORD
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
schemez/__init__.py,sha256=KkwJF8pfbzw_4cBxwXuUyXIExqdxS8iumvyAL-JUED4,1669
|
2
|
-
schemez/bind_kwargs.py,sha256=ChyArgNa5R8VdwSJmmrQItMH9Ld6hStWBISw-T1wyws,6228
|
3
|
-
schemez/code.py,sha256=usZLov9i5KpK1W2VJxngUzeetgrINtodiooG_AxN-y4,2072
|
4
|
-
schemez/convert.py,sha256=b6Sz11lq0HvpXfMREOqnnw8rcVg2XzTKhjjPNc4YIoE,4403
|
5
|
-
schemez/create_type.py,sha256=wrdqdzXtfxZfsgp9IroldoYGTJs_Rdli8TiscqhV2bI,11647
|
6
|
-
schemez/docstrings.py,sha256=kmd660wcomXzKac0SSNYxPRNbVCUovrpmE9jwnVRS6c,4115
|
7
|
-
schemez/executable.py,sha256=YM4WcmRyJ9CpzKpNgS0A-Ri0Jd7mAzfHmoaXONI-mIs,7134
|
8
|
-
schemez/functionschema.py,sha256=Z8kL1ckU6pEoRhOsFY6NziLhVudELHyylsFVXrCUcwU,26127
|
9
|
-
schemez/helpers.py,sha256=IVuoFbzPJs3eqJBrr0BA6SIHncoU6BFJGP41zTijzXM,8716
|
10
|
-
schemez/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
|
-
schemez/pydantic_types.py,sha256=8vgSl8i2z9n0fB-8AJj-D3TBByEWE5IxItBxQ0XwXFI,1640
|
12
|
-
schemez/schema.py,sha256=u6SDhYDtfCjgy2Aa-_MDLLNcUfEXbeye4T-W6s3AED8,9558
|
13
|
-
schemez/schema_generators.py,sha256=Gze7S7dQkTsl_1ckeHLXPxx4jQo7RB6hHQM-5fpAsrA,6973
|
14
|
-
schemez/schemadef/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
|
-
schemez/schemadef/schemadef.py,sha256=FtD7TOnYxiuYOIfadRHKkkbZn98mWFb0_lKfPsPR-hI,14393
|
16
|
-
schemez/typedefs.py,sha256=kxh-Vc5eKvCShdznJrg5N4_Fr8g1kYlkadEAdzgQ43w,6091
|
17
|
-
schemez-1.2.0.dist-info/WHEEL,sha256=I8-bO5cg2sb8TH6ZM6EgCP87Y1cV_f9UGgWnfAhVOZI,78
|
18
|
-
schemez-1.2.0.dist-info/METADATA,sha256=Nao7AGsQ0CGUiHDXT2nSE0AMOnwqlMybd8KTQSAaIB0,5399
|
19
|
-
schemez-1.2.0.dist-info/RECORD,,
|