37
37
from pydantic import BaseModel , field_validator
38
38
39
39
from contextgem .internal .loggers import logger
40
+ from contextgem .internal .typings .strings_to_types import _deserialize_type_hint
41
+ from contextgem .internal .typings .types_normalization import _normalize_type_annotation
42
+ from contextgem .internal .typings .types_to_strings import _serialize_type_hint
40
43
41
44
if TYPE_CHECKING :
42
45
from contextgem .internal .base .concepts import _Concept
@@ -101,7 +104,6 @@ def to_dict(self) -> dict[str, Any]:
101
104
"""
102
105
103
106
from contextgem .internal .data_models import _LLMCost , _LLMUsage
104
- from contextgem .internal .typings .types_to_strings import _serialize_type_hint
105
107
from contextgem .public .llms import DocumentLLM , DocumentLLMGroup
106
108
107
109
if isinstance (self , (DocumentLLM , DocumentLLMGroup )):
@@ -128,9 +130,8 @@ def to_dict(self) -> dict[str, Any]:
128
130
base_dict [key ] = [i .to_dict () for i in val ]
129
131
130
132
elif key == KEY_STRUCTURE_PUBLIC :
131
- # json object concept
132
- # Convert each item in the structure dict using the type-hint serializer
133
- base_dict [key ] = {k : _serialize_type_hint (v ) for k , v in val .items ()}
133
+ # Handle structure serialization for JsonObjectConcept structure
134
+ base_dict [key ] = self ._serialize_structure_dict (val )
134
135
135
136
elif key == KEY_RATING_SCALE_PUBLIC :
136
137
base_dict [key ] = val .to_dict ()
@@ -183,6 +184,45 @@ def to_dict(self) -> dict[str, Any]:
183
184
184
185
return {** base_dict }
185
186
187
+ def _serialize_structure_dict (
188
+ self , structure_dict : dict [str , Any ]
189
+ ) -> dict [str , Any ]:
190
+ """
191
+ Relevant for JsonObjectConcept structure serialization.
192
+
193
+ Recursively serializes a dictionary containing type hints to ensure proper serialization.
194
+ Handles nested dictionaries, lists of dictionaries, and various type hints.
195
+
196
+ :param structure_dict: Dictionary containing type hints to serialize
197
+ :type structure_dict: dict[str, Any]
198
+ :return: Dictionary with serialized type hints
199
+ :rtype: dict[str, Any]
200
+ """
201
+ result = {}
202
+ for key , value in structure_dict .items ():
203
+ # Normalize the value for consistent type representation
204
+ value = _normalize_type_annotation (value )
205
+
206
+ # Handle nested dictionaries
207
+ if isinstance (value , dict ):
208
+ # Class structs (if passed) are already converted to a dict structure
209
+ # during JsonObjectConcept initialization.
210
+ result [key ] = self ._serialize_structure_dict (value )
211
+ # Handle list of dictionaries (only need to serialize the first item)
212
+ elif (
213
+ isinstance (value , list )
214
+ and len (value ) == 1
215
+ and isinstance (value [0 ], dict )
216
+ ):
217
+ # Class structs (if passed) are already converted to a dict structure
218
+ # during JsonObjectConcept initialization.
219
+ result [key ] = [self ._serialize_structure_dict (value [0 ])]
220
+ # Other cases
221
+ else :
222
+ result [key ] = _serialize_type_hint (value )
223
+
224
+ return result
225
+
186
226
def _convert_decimal_to_float (self , obj : Any ) -> Any :
187
227
"""
188
228
Recursively converts Decimal objects to floats for JSON serialization.
@@ -288,7 +328,6 @@ def from_dict(cls, obj_dict: dict[str, Any]) -> Self:
288
328
import contextgem .public .examples as cg_examples
289
329
from contextgem import Image
290
330
from contextgem .internal .data_models import _LLMUsage
291
- from contextgem .internal .typings .strings_to_types import _deserialize_type_hint
292
331
from contextgem .public .aspects import Aspect
293
332
from contextgem .public .data_models import LLMPricing , RatingScale
294
333
from contextgem .public .llms import DocumentLLM
@@ -324,6 +363,47 @@ def lambda_list_val(
324
363
for d in val
325
364
]
326
365
366
+ def _deserialize_structure_dict (
367
+ structure_dict : dict [str , Any ],
368
+ ) -> dict [str , Any ]:
369
+ """
370
+ Relevant for JsonObjectConcept structure deserialization.
371
+
372
+ Recursively deserializes a dictionary containing string representations of type hints
373
+ into actual Python type objects. Handles nested dictionaries, lists of dictionaries,
374
+ and various type hint formats.
375
+
376
+ :param structure_dict: Dictionary containing serialized type hints to deserialize
377
+ :type structure_dict: dict[str, Any]
378
+ :return: Dictionary with deserialized type hints
379
+ :rtype: dict[str, Any]
380
+ """
381
+
382
+ result = {}
383
+ for k , v in structure_dict .items ():
384
+ # Class structs (if passed) are already converted to a dict structure
385
+ # during JsonObjectConcept initialization.
386
+ if isinstance (v , dict ):
387
+ result [k ] = _deserialize_structure_dict (v )
388
+ elif isinstance (v , list ) and len (v ) == 1 and isinstance (v [0 ], dict ):
389
+ result [k ] = [_deserialize_structure_dict (v [0 ])]
390
+ elif isinstance (v , str ):
391
+ try :
392
+ # Deserialize the type hint
393
+ type_hint = _deserialize_type_hint (v )
394
+
395
+ # Normalize the type hint for consistent representation
396
+ # This converts between typing module generics and built-in equivalents
397
+ normalized_type = _normalize_type_annotation (type_hint )
398
+
399
+ result [k ] = normalized_type
400
+ except ValueError :
401
+ # Keep as string if can't deserialize
402
+ result [k ] = v
403
+ else :
404
+ result [k ] = v
405
+ return result
406
+
327
407
# Create a map for known keys → reconstruction logic
328
408
rebuild_map : dict [str , Callable [[Any ], Any ]] = {
329
409
# Public attrs
@@ -333,9 +413,10 @@ def lambda_list_val(
333
413
KEY_PARAGRAPHS_PUBLIC : lambda_list_val (instance_cls = Paragraph ),
334
414
KEY_SENTENCES_PUBLIC : lambda_list_val (instance_cls = Sentence ),
335
415
KEY_IMAGES_PUBLIC : lambda_list_val (instance_cls = Image ),
336
- KEY_STRUCTURE_PUBLIC : lambda val : {
337
- k : _deserialize_type_hint (v ) for k , v in val .items ()
338
- },
416
+ KEY_STRUCTURE_PUBLIC : lambda val : (
417
+ # JsonObjectConcept structure is always converted to a dict
418
+ _deserialize_structure_dict (val )
419
+ ),
339
420
KEY_RATING_SCALE_PUBLIC : lambda val : RatingScale .from_dict (val ),
340
421
# LLM attrs
341
422
KEY_LLM_PRICING_PUBLIC : lambda val : (
0 commit comments