hammad-python 0.0.29__py3-none-any.whl → 0.0.31__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.
- ham/__init__.py +10 -0
- {hammad_python-0.0.29.dist-info → hammad_python-0.0.31.dist-info}/METADATA +6 -32
- hammad_python-0.0.31.dist-info/RECORD +6 -0
- hammad/__init__.py +0 -84
- hammad/_internal.py +0 -256
- hammad/_main.py +0 -226
- hammad/cache/__init__.py +0 -40
- hammad/cache/base_cache.py +0 -181
- hammad/cache/cache.py +0 -169
- hammad/cache/decorators.py +0 -261
- hammad/cache/file_cache.py +0 -80
- hammad/cache/ttl_cache.py +0 -74
- hammad/cli/__init__.py +0 -33
- hammad/cli/animations.py +0 -573
- hammad/cli/plugins.py +0 -867
- hammad/cli/styles/__init__.py +0 -55
- hammad/cli/styles/settings.py +0 -139
- hammad/cli/styles/types.py +0 -358
- hammad/cli/styles/utils.py +0 -634
- hammad/data/__init__.py +0 -90
- hammad/data/collections/__init__.py +0 -49
- hammad/data/collections/collection.py +0 -326
- hammad/data/collections/indexes/__init__.py +0 -37
- hammad/data/collections/indexes/qdrant/__init__.py +0 -1
- hammad/data/collections/indexes/qdrant/index.py +0 -723
- hammad/data/collections/indexes/qdrant/settings.py +0 -94
- hammad/data/collections/indexes/qdrant/utils.py +0 -210
- hammad/data/collections/indexes/tantivy/__init__.py +0 -1
- hammad/data/collections/indexes/tantivy/index.py +0 -426
- hammad/data/collections/indexes/tantivy/settings.py +0 -40
- hammad/data/collections/indexes/tantivy/utils.py +0 -176
- hammad/data/configurations/__init__.py +0 -35
- hammad/data/configurations/configuration.py +0 -564
- hammad/data/models/__init__.py +0 -50
- hammad/data/models/extensions/__init__.py +0 -4
- hammad/data/models/extensions/pydantic/__init__.py +0 -42
- hammad/data/models/extensions/pydantic/converters.py +0 -759
- hammad/data/models/fields.py +0 -546
- hammad/data/models/model.py +0 -1078
- hammad/data/models/utils.py +0 -280
- hammad/data/sql/__init__.py +0 -24
- hammad/data/sql/database.py +0 -576
- hammad/data/sql/types.py +0 -127
- hammad/data/types/__init__.py +0 -75
- hammad/data/types/file.py +0 -431
- hammad/data/types/multimodal/__init__.py +0 -36
- hammad/data/types/multimodal/audio.py +0 -200
- hammad/data/types/multimodal/image.py +0 -182
- hammad/data/types/text.py +0 -1308
- hammad/formatting/__init__.py +0 -33
- hammad/formatting/json/__init__.py +0 -27
- hammad/formatting/json/converters.py +0 -158
- hammad/formatting/text/__init__.py +0 -63
- hammad/formatting/text/converters.py +0 -723
- hammad/formatting/text/markdown.py +0 -131
- hammad/formatting/yaml/__init__.py +0 -26
- hammad/formatting/yaml/converters.py +0 -5
- hammad/genai/__init__.py +0 -217
- hammad/genai/a2a/__init__.py +0 -32
- hammad/genai/a2a/workers.py +0 -552
- hammad/genai/agents/__init__.py +0 -59
- hammad/genai/agents/agent.py +0 -1973
- hammad/genai/agents/run.py +0 -1024
- hammad/genai/agents/types/__init__.py +0 -42
- hammad/genai/agents/types/agent_context.py +0 -13
- hammad/genai/agents/types/agent_event.py +0 -128
- hammad/genai/agents/types/agent_hooks.py +0 -220
- hammad/genai/agents/types/agent_messages.py +0 -31
- hammad/genai/agents/types/agent_response.py +0 -125
- hammad/genai/agents/types/agent_stream.py +0 -327
- hammad/genai/graphs/__init__.py +0 -125
- hammad/genai/graphs/_utils.py +0 -190
- hammad/genai/graphs/base.py +0 -1828
- hammad/genai/graphs/plugins.py +0 -316
- hammad/genai/graphs/types.py +0 -638
- hammad/genai/models/__init__.py +0 -1
- hammad/genai/models/embeddings/__init__.py +0 -43
- hammad/genai/models/embeddings/model.py +0 -226
- hammad/genai/models/embeddings/run.py +0 -163
- hammad/genai/models/embeddings/types/__init__.py +0 -37
- hammad/genai/models/embeddings/types/embedding_model_name.py +0 -75
- hammad/genai/models/embeddings/types/embedding_model_response.py +0 -76
- hammad/genai/models/embeddings/types/embedding_model_run_params.py +0 -66
- hammad/genai/models/embeddings/types/embedding_model_settings.py +0 -47
- hammad/genai/models/language/__init__.py +0 -57
- hammad/genai/models/language/model.py +0 -1098
- hammad/genai/models/language/run.py +0 -878
- hammad/genai/models/language/types/__init__.py +0 -40
- hammad/genai/models/language/types/language_model_instructor_mode.py +0 -47
- hammad/genai/models/language/types/language_model_messages.py +0 -28
- hammad/genai/models/language/types/language_model_name.py +0 -239
- hammad/genai/models/language/types/language_model_request.py +0 -127
- hammad/genai/models/language/types/language_model_response.py +0 -217
- hammad/genai/models/language/types/language_model_response_chunk.py +0 -56
- hammad/genai/models/language/types/language_model_settings.py +0 -89
- hammad/genai/models/language/types/language_model_stream.py +0 -600
- hammad/genai/models/language/utils/__init__.py +0 -28
- hammad/genai/models/language/utils/requests.py +0 -421
- hammad/genai/models/language/utils/structured_outputs.py +0 -135
- hammad/genai/models/model_provider.py +0 -4
- hammad/genai/models/multimodal.py +0 -47
- hammad/genai/models/reranking.py +0 -26
- hammad/genai/types/__init__.py +0 -1
- hammad/genai/types/base.py +0 -215
- hammad/genai/types/history.py +0 -290
- hammad/genai/types/tools.py +0 -507
- hammad/logging/__init__.py +0 -35
- hammad/logging/decorators.py +0 -834
- hammad/logging/logger.py +0 -1018
- hammad/mcp/__init__.py +0 -53
- hammad/mcp/client/__init__.py +0 -35
- hammad/mcp/client/client.py +0 -624
- hammad/mcp/client/client_service.py +0 -400
- hammad/mcp/client/settings.py +0 -178
- hammad/mcp/servers/__init__.py +0 -26
- hammad/mcp/servers/launcher.py +0 -1161
- hammad/runtime/__init__.py +0 -32
- hammad/runtime/decorators.py +0 -142
- hammad/runtime/run.py +0 -299
- hammad/service/__init__.py +0 -49
- hammad/service/create.py +0 -527
- hammad/service/decorators.py +0 -283
- hammad/types.py +0 -288
- hammad/typing/__init__.py +0 -435
- hammad/web/__init__.py +0 -43
- hammad/web/http/__init__.py +0 -1
- hammad/web/http/client.py +0 -944
- hammad/web/models.py +0 -275
- hammad/web/openapi/__init__.py +0 -1
- hammad/web/openapi/client.py +0 -740
- hammad/web/search/__init__.py +0 -1
- hammad/web/search/client.py +0 -1023
- hammad/web/utils.py +0 -472
- hammad_python-0.0.29.dist-info/RECORD +0 -135
- {hammad → ham}/py.typed +0 -0
- {hammad_python-0.0.29.dist-info → hammad_python-0.0.31.dist-info}/WHEEL +0 -0
- {hammad_python-0.0.29.dist-info → hammad_python-0.0.31.dist-info}/licenses/LICENSE +0 -0
@@ -1,759 +0,0 @@
|
|
1
|
-
"""hammad.data.models.extensions.pydantic.converters
|
2
|
-
|
3
|
-
Contains various converters for converting various objects into
|
4
|
-
a Pydantic model. These converters are used to convert
|
5
|
-
various objects into a Pydantic model, such as types,
|
6
|
-
docstrings, and other objects."""
|
7
|
-
|
8
|
-
import inspect
|
9
|
-
import logging
|
10
|
-
from dataclasses import is_dataclass, fields as dataclass_fields, MISSING
|
11
|
-
from docstring_parser import parse
|
12
|
-
from typing import (
|
13
|
-
Any,
|
14
|
-
Dict,
|
15
|
-
Literal,
|
16
|
-
Optional,
|
17
|
-
Type,
|
18
|
-
Union,
|
19
|
-
Mapping,
|
20
|
-
get_type_hints,
|
21
|
-
Callable,
|
22
|
-
Sequence,
|
23
|
-
TypeVar,
|
24
|
-
Tuple,
|
25
|
-
List,
|
26
|
-
overload,
|
27
|
-
cast,
|
28
|
-
)
|
29
|
-
from pydantic import BaseModel, Field, create_model
|
30
|
-
|
31
|
-
from typing import get_origin, get_args
|
32
|
-
from typing_inspect import is_generic_type
|
33
|
-
|
34
|
-
logger = logging.getLogger(__name__)
|
35
|
-
|
36
|
-
__all__ = [
|
37
|
-
"is_pydantic_model_class",
|
38
|
-
"get_pydantic_fields_from_function",
|
39
|
-
"convert_to_pydantic_field",
|
40
|
-
"convert_to_pydantic_model",
|
41
|
-
"create_selection_pydantic_model",
|
42
|
-
"create_confirmation_pydantic_model",
|
43
|
-
"convert_dataclass_to_pydantic_model",
|
44
|
-
"convert_type_to_pydantic_model",
|
45
|
-
"convert_function_to_pydantic_model",
|
46
|
-
"convert_sequence_to_pydantic_model",
|
47
|
-
"convert_dict_to_pydantic_model",
|
48
|
-
]
|
49
|
-
|
50
|
-
|
51
|
-
# -----------------------------------------------------------------------------
|
52
|
-
# Types & Constants
|
53
|
-
# -----------------------------------------------------------------------------
|
54
|
-
|
55
|
-
BaseModelType = TypeVar("BaseModelType", bound=BaseModel)
|
56
|
-
"""Helper type for Pydantic model classes."""
|
57
|
-
|
58
|
-
|
59
|
-
JSON_TYPE_MAPPING: Mapping[Any, Tuple[str, Any]] = {
|
60
|
-
int: ("int", int),
|
61
|
-
float: ("float", float),
|
62
|
-
bool: ("bool", bool),
|
63
|
-
str: ("str", str),
|
64
|
-
bytes: ("bytes", bytes),
|
65
|
-
list: ("list", list),
|
66
|
-
tuple: ("tuple", tuple),
|
67
|
-
dict: ("dict", dict),
|
68
|
-
set: ("set", set),
|
69
|
-
frozenset: ("frozenset", frozenset),
|
70
|
-
Any: ("any", Any),
|
71
|
-
None: ("none", None),
|
72
|
-
Union: ("union", Union),
|
73
|
-
Optional: ("optional", Optional),
|
74
|
-
}
|
75
|
-
"""
|
76
|
-
A mapping of types to their string representations. Used for hinting & JSON schema
|
77
|
-
generation.
|
78
|
-
"""
|
79
|
-
|
80
|
-
TYPE_NAME_MAPPING: Dict[Type, str] = {
|
81
|
-
int: "Integer",
|
82
|
-
float: "Float",
|
83
|
-
bool: "Boolean",
|
84
|
-
str: "String",
|
85
|
-
bytes: "Bytes",
|
86
|
-
list: "List",
|
87
|
-
tuple: "Tuple",
|
88
|
-
dict: "Dict",
|
89
|
-
set: "Set",
|
90
|
-
frozenset: "FrozenSet",
|
91
|
-
}
|
92
|
-
"""
|
93
|
-
A mapping of basic types to their semantic model names.
|
94
|
-
"""
|
95
|
-
|
96
|
-
FIELD_NAME_MAPPING: Dict[Type, str] = {
|
97
|
-
int: "value",
|
98
|
-
float: "value",
|
99
|
-
bool: "flag",
|
100
|
-
str: "text",
|
101
|
-
bytes: "data",
|
102
|
-
list: "items",
|
103
|
-
tuple: "values",
|
104
|
-
dict: "mapping",
|
105
|
-
set: "elements",
|
106
|
-
frozenset: "elements",
|
107
|
-
}
|
108
|
-
"""
|
109
|
-
A mapping of basic types to their semantic field names.
|
110
|
-
"""
|
111
|
-
|
112
|
-
|
113
|
-
# -----------------------------------------------------------------------------
|
114
|
-
# Pydantic Model Utils
|
115
|
-
# -----------------------------------------------------------------------------
|
116
|
-
|
117
|
-
|
118
|
-
def is_pydantic_model_class(obj: Any) -> bool:
|
119
|
-
"""
|
120
|
-
Checks if an object is a Pydantic model class.
|
121
|
-
"""
|
122
|
-
return isinstance(obj, type) and issubclass(obj, BaseModel)
|
123
|
-
|
124
|
-
|
125
|
-
def generate_semantic_model_name(type_hint: Type) -> str:
|
126
|
-
"""
|
127
|
-
Generates a semantic model name based on the type.
|
128
|
-
|
129
|
-
Examples:
|
130
|
-
int -> "Integer"
|
131
|
-
str -> "String"
|
132
|
-
List[int] -> "IntegerList"
|
133
|
-
Dict[str, int] -> "StringIntegerDict"
|
134
|
-
Optional[int] -> "OptionalInteger"
|
135
|
-
Union[str, int] -> "StringIntegerUnion"
|
136
|
-
"""
|
137
|
-
# Handle basic types
|
138
|
-
if type_hint in TYPE_NAME_MAPPING:
|
139
|
-
return TYPE_NAME_MAPPING[type_hint]
|
140
|
-
|
141
|
-
# Handle Optional types
|
142
|
-
origin = get_origin(type_hint)
|
143
|
-
args = get_args(type_hint)
|
144
|
-
|
145
|
-
if origin is Union:
|
146
|
-
# Check if it's Optional (Union with None)
|
147
|
-
if type(None) in args and len(args) == 2:
|
148
|
-
# This is Optional[T]
|
149
|
-
inner_type = next(arg for arg in args if arg is not type(None))
|
150
|
-
return f"Optional{generate_semantic_model_name(inner_type)}"
|
151
|
-
else:
|
152
|
-
# Regular Union
|
153
|
-
type_names = [
|
154
|
-
generate_semantic_model_name(arg)
|
155
|
-
for arg in args
|
156
|
-
if arg is not type(None)
|
157
|
-
]
|
158
|
-
return "".join(type_names) + "Union"
|
159
|
-
|
160
|
-
# Handle generic types
|
161
|
-
if origin is not None:
|
162
|
-
origin_name = TYPE_NAME_MAPPING.get(origin, origin.__name__.capitalize())
|
163
|
-
|
164
|
-
if args:
|
165
|
-
# Generate names for type arguments
|
166
|
-
arg_names = [generate_semantic_model_name(arg) for arg in args]
|
167
|
-
if origin in (list, set, frozenset):
|
168
|
-
# For collections, append the element type
|
169
|
-
return f"{arg_names[0]}{origin_name}"
|
170
|
-
elif origin is dict:
|
171
|
-
# For dict, include both key and value types
|
172
|
-
return f"{arg_names[0]}{arg_names[1]}Dict"
|
173
|
-
elif origin is tuple:
|
174
|
-
# For tuple, join all types
|
175
|
-
return "".join(arg_names) + "Tuple"
|
176
|
-
else:
|
177
|
-
# For other generics, prepend type arguments
|
178
|
-
return "".join(arg_names) + origin_name
|
179
|
-
|
180
|
-
return origin_name
|
181
|
-
|
182
|
-
# Fallback to the type's name
|
183
|
-
if hasattr(type_hint, "__name__"):
|
184
|
-
return type_hint.__name__.capitalize()
|
185
|
-
|
186
|
-
return "GeneratedModel"
|
187
|
-
|
188
|
-
|
189
|
-
def generate_semantic_field_name(type_hint: Type) -> str:
|
190
|
-
"""
|
191
|
-
Generates a semantic field name based on the type.
|
192
|
-
|
193
|
-
Examples:
|
194
|
-
int -> "value"
|
195
|
-
str -> "text"
|
196
|
-
List[int] -> "items"
|
197
|
-
Dict[str, int] -> "mapping"
|
198
|
-
Optional[str] -> "text"
|
199
|
-
"""
|
200
|
-
# Handle basic types
|
201
|
-
if type_hint in FIELD_NAME_MAPPING:
|
202
|
-
return FIELD_NAME_MAPPING[type_hint]
|
203
|
-
|
204
|
-
# Handle Optional types - use the inner type's field name
|
205
|
-
origin = get_origin(type_hint)
|
206
|
-
args = get_args(type_hint)
|
207
|
-
|
208
|
-
if origin is Union:
|
209
|
-
# Check if it's Optional (Union with None)
|
210
|
-
if type(None) in args and len(args) == 2:
|
211
|
-
# This is Optional[T] - use inner type's field name
|
212
|
-
inner_type = next(arg for arg in args if arg is not type(None))
|
213
|
-
return generate_semantic_field_name(inner_type)
|
214
|
-
|
215
|
-
# Handle generic types - use the origin's field name
|
216
|
-
if origin is not None and origin in FIELD_NAME_MAPPING:
|
217
|
-
return FIELD_NAME_MAPPING[origin]
|
218
|
-
|
219
|
-
# Default fallback
|
220
|
-
return "value"
|
221
|
-
|
222
|
-
|
223
|
-
def get_pydantic_fields_from_function(func: Callable) -> Dict[str, Tuple[Type, Field]]:
|
224
|
-
"""
|
225
|
-
Extracts Pydantic fields from a function's signature and docstring.
|
226
|
-
Returns a dictionary mapping field names to (type, Pydantic Field) tuples.
|
227
|
-
|
228
|
-
Args:
|
229
|
-
func: The function to extract Pydantic fields from.
|
230
|
-
|
231
|
-
Returns:
|
232
|
-
A dictionary mapping field names to (type, Pydantic Field) tuples.
|
233
|
-
"""
|
234
|
-
try:
|
235
|
-
hints = get_type_hints(func)
|
236
|
-
fields_dict: Dict[str, Tuple[Type, Field]] = {}
|
237
|
-
doc_info = parse(func.__doc__ or "")
|
238
|
-
|
239
|
-
for param_name, param_type in hints.items():
|
240
|
-
if param_name == "return":
|
241
|
-
continue
|
242
|
-
|
243
|
-
description = ""
|
244
|
-
if doc_info.params:
|
245
|
-
description = (
|
246
|
-
next(
|
247
|
-
(
|
248
|
-
p.description
|
249
|
-
for p in doc_info.params
|
250
|
-
if p.arg_name == param_name
|
251
|
-
),
|
252
|
-
"",
|
253
|
-
)
|
254
|
-
or ""
|
255
|
-
)
|
256
|
-
|
257
|
-
default_value = ...
|
258
|
-
param = inspect.signature(func).parameters.get(param_name)
|
259
|
-
if param and param.default is not inspect.Parameter.empty:
|
260
|
-
default_value = param.default
|
261
|
-
|
262
|
-
fields_dict[param_name] = (
|
263
|
-
param_type,
|
264
|
-
Field(default=default_value, description=description),
|
265
|
-
)
|
266
|
-
return fields_dict
|
267
|
-
except Exception as e:
|
268
|
-
logger.error(
|
269
|
-
f"Error extracting function fields for {getattr(func, '__name__', 'unknown function')}: {e}"
|
270
|
-
)
|
271
|
-
return {}
|
272
|
-
|
273
|
-
|
274
|
-
def convert_to_pydantic_field(
|
275
|
-
type_hint: Type,
|
276
|
-
index: Optional[int] = None,
|
277
|
-
description: Optional[str] = None,
|
278
|
-
default: Any = ...,
|
279
|
-
) -> Dict[str, Tuple[Type, Field]]:
|
280
|
-
"""
|
281
|
-
Creates a Pydantic field definition from a type hint.
|
282
|
-
Returns a dictionary mapping a generated field name to its (type, Field) tuple.
|
283
|
-
"""
|
284
|
-
try:
|
285
|
-
# Use semantic field name if no index is provided
|
286
|
-
if index is None:
|
287
|
-
field_name = generate_semantic_field_name(type_hint)
|
288
|
-
else:
|
289
|
-
# Use indexed field name for sequences
|
290
|
-
base_name = generate_semantic_field_name(type_hint)
|
291
|
-
field_name = f"{base_name}_{index}"
|
292
|
-
|
293
|
-
return {
|
294
|
-
field_name: (
|
295
|
-
type_hint,
|
296
|
-
Field(default=default, description=description or ""),
|
297
|
-
)
|
298
|
-
}
|
299
|
-
except Exception as e:
|
300
|
-
logger.error(f"Error creating Pydantic field mapping for type {type_hint}: {e}")
|
301
|
-
raise
|
302
|
-
|
303
|
-
|
304
|
-
# -----------------------------------------------------------------------------
|
305
|
-
# Simplified Model Creation
|
306
|
-
# -----------------------------------------------------------------------------
|
307
|
-
|
308
|
-
|
309
|
-
# -----------------------------------------------------------------------------
|
310
|
-
# Helpers (Private)
|
311
|
-
# -----------------------------------------------------------------------------
|
312
|
-
|
313
|
-
|
314
|
-
def convert_dataclass_to_pydantic_model(
|
315
|
-
target: Union[Type, Any], # NOTE: DATACLASS TYPE OR INSTANCE
|
316
|
-
init: bool,
|
317
|
-
name: Optional[str],
|
318
|
-
description: Optional[str],
|
319
|
-
) -> Union[Type[BaseModel], BaseModel]:
|
320
|
-
# Determine if we're dealing with a type or instance
|
321
|
-
is_instance = not isinstance(target, type)
|
322
|
-
dataclass_type = type(target) if is_instance else target
|
323
|
-
|
324
|
-
model_name = name or dataclass_type.__name__
|
325
|
-
doc_info = parse(dataclass_type.__doc__ or "")
|
326
|
-
model_doc = description or doc_info.short_description
|
327
|
-
|
328
|
-
pydantic_fields: Dict[str, Tuple[Type, Field]] = {}
|
329
|
-
for dc_field in dataclass_fields(dataclass_type):
|
330
|
-
field_type = dc_field.type
|
331
|
-
field_default = dc_field.default if dc_field.default is not MISSING else ... # type: ignore
|
332
|
-
if dc_field.default_factory is not MISSING: # type: ignore
|
333
|
-
field_default = Field(default_factory=dc_field.default_factory)
|
334
|
-
|
335
|
-
field_description = ""
|
336
|
-
if doc_info.params:
|
337
|
-
field_description = (
|
338
|
-
next(
|
339
|
-
(
|
340
|
-
p.description
|
341
|
-
for p in doc_info.params
|
342
|
-
if p.arg_name == dc_field.name
|
343
|
-
),
|
344
|
-
"",
|
345
|
-
)
|
346
|
-
or ""
|
347
|
-
)
|
348
|
-
|
349
|
-
pydantic_fields[dc_field.name] = (
|
350
|
-
field_type,
|
351
|
-
Field(default=field_default, description=field_description),
|
352
|
-
)
|
353
|
-
|
354
|
-
model_class = create_model(model_name, __doc__=model_doc, **pydantic_fields)
|
355
|
-
|
356
|
-
if init and is_instance:
|
357
|
-
instance_data = {
|
358
|
-
f.name: getattr(target, f.name) for f in dataclass_fields(dataclass_type)
|
359
|
-
}
|
360
|
-
return model_class(**instance_data)
|
361
|
-
return model_class
|
362
|
-
|
363
|
-
|
364
|
-
def convert_type_to_pydantic_model(
|
365
|
-
target: Type,
|
366
|
-
name: Optional[str],
|
367
|
-
description: Optional[str],
|
368
|
-
field_name: Optional[str],
|
369
|
-
default: Any,
|
370
|
-
) -> Type[BaseModel]:
|
371
|
-
# Use semantic naming if no name is provided
|
372
|
-
model_name = name or generate_semantic_model_name(target)
|
373
|
-
|
374
|
-
field_mapping = convert_to_pydantic_field(
|
375
|
-
target, description=description, default=default
|
376
|
-
)
|
377
|
-
|
378
|
-
if field_name: # Override default field name if explicitly provided
|
379
|
-
current_field_def = list(field_mapping.values())[0]
|
380
|
-
field_mapping = {field_name: current_field_def}
|
381
|
-
|
382
|
-
return create_model(model_name, __doc__=(description or ""), **field_mapping)
|
383
|
-
|
384
|
-
|
385
|
-
def convert_function_to_pydantic_model(
|
386
|
-
target: Callable,
|
387
|
-
name: Optional[str],
|
388
|
-
description: Optional[str],
|
389
|
-
) -> Type[BaseModel]:
|
390
|
-
model_name = name or target.__name__
|
391
|
-
doc_info = parse(target.__doc__ or "")
|
392
|
-
model_doc = description or doc_info.short_description
|
393
|
-
|
394
|
-
fields = get_pydantic_fields_from_function(target)
|
395
|
-
return create_model(model_name, __doc__=model_doc, **fields)
|
396
|
-
|
397
|
-
|
398
|
-
def convert_sequence_to_pydantic_model(
|
399
|
-
target: Sequence[Type],
|
400
|
-
name: Optional[str],
|
401
|
-
description: Optional[str],
|
402
|
-
field_name: Optional[str],
|
403
|
-
default: Any,
|
404
|
-
) -> Type[BaseModel]:
|
405
|
-
if not target:
|
406
|
-
raise ValueError("Cannot create Pydantic model from empty sequence")
|
407
|
-
|
408
|
-
model_name = name or "GeneratedModel"
|
409
|
-
pydantic_fields: Dict[str, Tuple[Type, Field]] = {}
|
410
|
-
|
411
|
-
for i, type_hint in enumerate(target):
|
412
|
-
if not isinstance(type_hint, type):
|
413
|
-
raise ValueError(
|
414
|
-
f"Sequence elements must be types, got {type_hint} at index {i}"
|
415
|
-
)
|
416
|
-
|
417
|
-
field_desc = description if i == 0 and field_name else None
|
418
|
-
field_def_default = default if i == 0 and field_name else ...
|
419
|
-
|
420
|
-
# Use provided field_name for the first element if specified
|
421
|
-
current_field_name_override = field_name if i == 0 else None
|
422
|
-
|
423
|
-
# Generate field(s) from type_hint
|
424
|
-
temp_field_def = convert_to_pydantic_field(
|
425
|
-
type_hint,
|
426
|
-
index=None if current_field_name_override else i,
|
427
|
-
description=field_desc,
|
428
|
-
default=field_def_default,
|
429
|
-
)
|
430
|
-
|
431
|
-
actual_field_name = list(temp_field_def.keys())[0]
|
432
|
-
actual_type_info = list(temp_field_def.values())[0]
|
433
|
-
|
434
|
-
if current_field_name_override:
|
435
|
-
pydantic_fields[current_field_name_override] = actual_type_info
|
436
|
-
else:
|
437
|
-
pydantic_fields[actual_field_name] = actual_type_info
|
438
|
-
|
439
|
-
return create_model(model_name, __doc__=(description or ""), **pydantic_fields)
|
440
|
-
|
441
|
-
|
442
|
-
def convert_dict_to_pydantic_model(
|
443
|
-
target: Dict[str, Any],
|
444
|
-
init: bool,
|
445
|
-
name: Optional[str],
|
446
|
-
description: Optional[str],
|
447
|
-
) -> Union[Type[BaseModel], BaseModel]:
|
448
|
-
model_name = name or "GeneratedModel"
|
449
|
-
|
450
|
-
pydantic_fields: Dict[str, Tuple[Type, Field]] = {}
|
451
|
-
for k, v in target.items():
|
452
|
-
pydantic_fields[k] = (type(v), Field(default=v if init else ...))
|
453
|
-
|
454
|
-
model_class = create_model(
|
455
|
-
model_name, __doc__=(description or ""), **pydantic_fields
|
456
|
-
)
|
457
|
-
|
458
|
-
if init:
|
459
|
-
return model_class(**target)
|
460
|
-
return model_class
|
461
|
-
|
462
|
-
|
463
|
-
def _reconvert_to_pydantic_model_from_basemodel_instance(
|
464
|
-
target: BaseModel,
|
465
|
-
name: Optional[str],
|
466
|
-
description: Optional[str],
|
467
|
-
) -> BaseModel:
|
468
|
-
model_name = name or target.__class__.__name__
|
469
|
-
doc_info = parse(target.__class__.__doc__ or "")
|
470
|
-
model_doc = description or doc_info.short_description
|
471
|
-
|
472
|
-
instance_data = target.model_dump()
|
473
|
-
pydantic_fields: Dict[str, Tuple[Type, Field]] = {}
|
474
|
-
for k, v_instance in instance_data.items():
|
475
|
-
original_field_info = target.__class__.model_fields.get(k)
|
476
|
-
field_desc = original_field_info.description if original_field_info else ""
|
477
|
-
pydantic_fields[k] = (
|
478
|
-
type(v_instance),
|
479
|
-
Field(default=v_instance, description=field_desc),
|
480
|
-
)
|
481
|
-
|
482
|
-
new_model_class = create_model(model_name, __doc__=model_doc, **pydantic_fields)
|
483
|
-
return new_model_class(**instance_data)
|
484
|
-
|
485
|
-
|
486
|
-
# ITS OVER 9000
|
487
|
-
|
488
|
-
|
489
|
-
@overload
|
490
|
-
def convert_to_pydantic_model(
|
491
|
-
target: Type[BaseModelType],
|
492
|
-
init: Literal[False] = False,
|
493
|
-
name: Optional[str] = None,
|
494
|
-
description: Optional[str] = None,
|
495
|
-
field_name: Optional[str] = None,
|
496
|
-
default: Any = ...,
|
497
|
-
) -> Type[BaseModelType]: ...
|
498
|
-
@overload
|
499
|
-
def convert_to_pydantic_model(
|
500
|
-
target: Type[BaseModelType],
|
501
|
-
init: Literal[True],
|
502
|
-
name: Optional[str] = None,
|
503
|
-
description: Optional[str] = None,
|
504
|
-
field_name: Optional[str] = None,
|
505
|
-
default: Any = ...,
|
506
|
-
) -> BaseModelType: ...
|
507
|
-
@overload
|
508
|
-
def convert_to_pydantic_model(
|
509
|
-
target: BaseModelType,
|
510
|
-
init: Literal[False] = False,
|
511
|
-
name: Optional[str] = None,
|
512
|
-
description: Optional[str] = None,
|
513
|
-
field_name: Optional[str] = None,
|
514
|
-
default: Any = ...,
|
515
|
-
) -> Type[BaseModelType]: ...
|
516
|
-
@overload
|
517
|
-
def convert_to_pydantic_model(
|
518
|
-
target: BaseModelType,
|
519
|
-
init: Literal[True],
|
520
|
-
name: Optional[str] = None,
|
521
|
-
description: Optional[str] = None,
|
522
|
-
field_name: Optional[str] = None,
|
523
|
-
default: Any = ...,
|
524
|
-
) -> BaseModelType: ...
|
525
|
-
@overload
|
526
|
-
def convert_to_pydantic_model(
|
527
|
-
target: Type,
|
528
|
-
init: Literal[False] = False,
|
529
|
-
name: Optional[str] = None,
|
530
|
-
description: Optional[str] = None,
|
531
|
-
field_name: Optional[str] = None,
|
532
|
-
default: Any = ...,
|
533
|
-
) -> Type[BaseModel]: ...
|
534
|
-
@overload
|
535
|
-
def convert_to_pydantic_model(
|
536
|
-
target: Type,
|
537
|
-
init: Literal[True],
|
538
|
-
name: Optional[str] = None,
|
539
|
-
description: Optional[str] = None,
|
540
|
-
field_name: Optional[str] = None,
|
541
|
-
default: Any = ...,
|
542
|
-
) -> BaseModel: # For dataclass instances from type
|
543
|
-
...
|
544
|
-
@overload
|
545
|
-
def convert_to_pydantic_model(
|
546
|
-
target: Callable,
|
547
|
-
init: Literal[False] = False,
|
548
|
-
name: Optional[str] = None,
|
549
|
-
description: Optional[str] = None,
|
550
|
-
field_name: Optional[str] = None,
|
551
|
-
default: Any = ...,
|
552
|
-
) -> Type[BaseModel]: ...
|
553
|
-
@overload
|
554
|
-
def convert_to_pydantic_model(
|
555
|
-
target: Sequence[Type],
|
556
|
-
init: Literal[False] = False,
|
557
|
-
name: Optional[str] = None,
|
558
|
-
description: Optional[str] = None,
|
559
|
-
field_name: Optional[str] = None,
|
560
|
-
default: Any = ...,
|
561
|
-
) -> Type[BaseModel]: ...
|
562
|
-
@overload
|
563
|
-
def convert_to_pydantic_model(
|
564
|
-
target: Dict[str, Any],
|
565
|
-
init: Literal[False] = False,
|
566
|
-
name: Optional[str] = None,
|
567
|
-
description: Optional[str] = None,
|
568
|
-
field_name: Optional[str] = None,
|
569
|
-
default: Any = ...,
|
570
|
-
) -> Type[BaseModel]: ...
|
571
|
-
@overload
|
572
|
-
def convert_to_pydantic_model(
|
573
|
-
target: Dict[str, Any],
|
574
|
-
init: Literal[True],
|
575
|
-
name: Optional[str] = None,
|
576
|
-
description: Optional[str] = None,
|
577
|
-
field_name: Optional[str] = None,
|
578
|
-
default: Any = ...,
|
579
|
-
) -> BaseModel: ...
|
580
|
-
|
581
|
-
|
582
|
-
def convert_to_pydantic_model(
|
583
|
-
target: Union[Type, Sequence[Type], Dict[str, Any], BaseModel, Callable],
|
584
|
-
init: bool = False,
|
585
|
-
name: Optional[str] = None,
|
586
|
-
description: Optional[str] = None,
|
587
|
-
field_name: Optional[str] = None,
|
588
|
-
default: Any = ...,
|
589
|
-
) -> Union[Type[BaseModel], BaseModel]:
|
590
|
-
"""
|
591
|
-
Converts various input types into a Pydantic model class or instance.
|
592
|
-
|
593
|
-
Args:
|
594
|
-
target: The target to convert (Python type, Pydantic BaseModel class/instance,
|
595
|
-
dataclass type/instance, function, sequence of types, or dict).
|
596
|
-
init: If True, returns an initialized Pydantic model instance where applicable
|
597
|
-
(e.g., from a dict, dataclass instance, or BaseModel instance).
|
598
|
-
If False (default), returns a Pydantic model class.
|
599
|
-
name: Optional name for the generated Pydantic model.
|
600
|
-
description: Optional description for the model (used as its docstring).
|
601
|
-
field_name: Optional name for the primary field if `target` is a single type
|
602
|
-
or for the first field if `target` is a sequence of types.
|
603
|
-
default: Optional default value if `target` is a single type, used with `field_name`.
|
604
|
-
|
605
|
-
Returns:
|
606
|
-
A Pydantic model class, or an instance of one if `init` is True and applicable.
|
607
|
-
"""
|
608
|
-
# Handle existing Pydantic model classes
|
609
|
-
if is_pydantic_model_class(target):
|
610
|
-
target_cls = cast(Type[BaseModel], target)
|
611
|
-
if init:
|
612
|
-
try:
|
613
|
-
return target_cls()
|
614
|
-
except Exception as e:
|
615
|
-
logger.warning(
|
616
|
-
f"Cannot auto-initialize {target_cls.__name__} due to missing required fields: {e}"
|
617
|
-
)
|
618
|
-
# Cannot create instance without required fields, return the class instead
|
619
|
-
return target_cls
|
620
|
-
if name and name != target_cls.__name__ or description:
|
621
|
-
return _reconvert_to_pydantic_model_from_basemodel_instance(
|
622
|
-
target_cls(), name=name, description=description
|
623
|
-
)
|
624
|
-
return target_cls
|
625
|
-
|
626
|
-
# Handle Pydantic model instances
|
627
|
-
if isinstance(target, BaseModel):
|
628
|
-
if init:
|
629
|
-
return _reconvert_to_pydantic_model_from_basemodel_instance(
|
630
|
-
target, name=name, description=description
|
631
|
-
)
|
632
|
-
return target.__class__
|
633
|
-
|
634
|
-
# Handle dataclasses (types or instances)
|
635
|
-
if is_dataclass(target):
|
636
|
-
if isinstance(target, type):
|
637
|
-
# target is a dataclass type
|
638
|
-
return convert_dataclass_to_pydantic_model(
|
639
|
-
cast(Type, target),
|
640
|
-
init=init,
|
641
|
-
name=name,
|
642
|
-
description=description,
|
643
|
-
)
|
644
|
-
else:
|
645
|
-
# target is a dataclass instance
|
646
|
-
return convert_dataclass_to_pydantic_model(
|
647
|
-
target, # Pass the instance directly
|
648
|
-
init=init,
|
649
|
-
name=name,
|
650
|
-
description=description,
|
651
|
-
)
|
652
|
-
|
653
|
-
# Handle generic types (like list[str], dict[str, int], etc.)
|
654
|
-
if is_generic_type(target) or get_origin(target) is not None:
|
655
|
-
return convert_type_to_pydantic_model(
|
656
|
-
target, name, description, field_name, default
|
657
|
-
)
|
658
|
-
|
659
|
-
# Handle standard Python types (int, str, etc.)
|
660
|
-
if isinstance(target, type):
|
661
|
-
return convert_type_to_pydantic_model(
|
662
|
-
target, name, description, field_name, default
|
663
|
-
)
|
664
|
-
|
665
|
-
# Handle callables (functions)
|
666
|
-
if callable(target):
|
667
|
-
return convert_function_to_pydantic_model(target, name, description)
|
668
|
-
|
669
|
-
# Handle sequences of types
|
670
|
-
if isinstance(target, Sequence) and not isinstance(target, str):
|
671
|
-
if not all(isinstance(t, type) for t in target):
|
672
|
-
raise TypeError("If target is a sequence, all its elements must be types.")
|
673
|
-
return convert_sequence_to_pydantic_model(
|
674
|
-
cast(Sequence[Type], target), name, description, field_name, default
|
675
|
-
)
|
676
|
-
|
677
|
-
# Handle dictionaries
|
678
|
-
if isinstance(target, dict):
|
679
|
-
return convert_dict_to_pydantic_model(target, init, name, description)
|
680
|
-
|
681
|
-
else:
|
682
|
-
logger.error(
|
683
|
-
f"Unsupported target type for Pydantic model creation: {type(target)} | how did you get here?"
|
684
|
-
)
|
685
|
-
raise TypeError(
|
686
|
-
f"Cannot create Pydantic model from target of type {type(target)}"
|
687
|
-
)
|
688
|
-
|
689
|
-
|
690
|
-
# -----------------------------------------------------------------------------
|
691
|
-
# Specialized Model Creation Utils
|
692
|
-
# -----------------------------------------------------------------------------
|
693
|
-
|
694
|
-
|
695
|
-
def create_selection_pydantic_model(
|
696
|
-
fields: List[str],
|
697
|
-
name: str = "Selection",
|
698
|
-
description: Optional[str] = None,
|
699
|
-
) -> Type[BaseModel]:
|
700
|
-
"""
|
701
|
-
Creates a Pydantic model for making a selection from a list of string options.
|
702
|
-
The model will have a single field named `selection` of type `Literal[*fields]`.
|
703
|
-
|
704
|
-
Args:
|
705
|
-
fields: A list of strings representing the allowed choices. Must not be empty.
|
706
|
-
name: The name for the created Pydantic model.
|
707
|
-
description: Optional description for the model (becomes its docstring).
|
708
|
-
|
709
|
-
Returns:
|
710
|
-
A new Pydantic BaseModel class with a 'selection' field.
|
711
|
-
Raises:
|
712
|
-
ValueError: If `fields` is empty.
|
713
|
-
"""
|
714
|
-
if not fields:
|
715
|
-
raise ValueError(
|
716
|
-
"`fields` list cannot be empty for `create_selection_pydantic_model`."
|
717
|
-
)
|
718
|
-
|
719
|
-
literal_args_str = ", ".join(repr(str(f)) for f in fields)
|
720
|
-
selection_type = eval(f"Literal[{literal_args_str}]")
|
721
|
-
|
722
|
-
model_fields_definitions = {
|
723
|
-
"selection": (
|
724
|
-
selection_type,
|
725
|
-
Field(..., description="The selected value from the available options."),
|
726
|
-
)
|
727
|
-
}
|
728
|
-
model_docstring = (
|
729
|
-
description or f"A model for selecting one option from: {', '.join(fields)}."
|
730
|
-
)
|
731
|
-
return create_model(
|
732
|
-
name, __base__=BaseModel, __doc__=model_docstring, **model_fields_definitions
|
733
|
-
)
|
734
|
-
|
735
|
-
|
736
|
-
def create_confirmation_pydantic_model(
|
737
|
-
name: str = "Confirmation",
|
738
|
-
description: Optional[str] = None,
|
739
|
-
field_name: str = "confirmed",
|
740
|
-
) -> Type[BaseModel]:
|
741
|
-
"""
|
742
|
-
Creates a Pydantic model for a boolean confirmation.
|
743
|
-
The model will have a single boolean field.
|
744
|
-
|
745
|
-
Args:
|
746
|
-
name: The name for the created Pydantic model.
|
747
|
-
description: Optional description for the model.
|
748
|
-
field_name: Name of the boolean field in the model.
|
749
|
-
|
750
|
-
Returns:
|
751
|
-
A new Pydantic BaseModel class.
|
752
|
-
"""
|
753
|
-
model_fields_definitions = {
|
754
|
-
field_name: (bool, Field(..., description="The boolean confirmation value."))
|
755
|
-
}
|
756
|
-
model_docstring = description or "A model for boolean confirmation."
|
757
|
-
return create_model(
|
758
|
-
name, __base__=BaseModel, __doc__=model_docstring, **model_fields_definitions
|
759
|
-
)
|