openai-sdk-helpers 0.2.0__py3-none-any.whl → 0.4.0__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.
Files changed (58) hide show
  1. openai_sdk_helpers/__init__.py +6 -6
  2. openai_sdk_helpers/agent/__init__.py +4 -2
  3. openai_sdk_helpers/agent/base.py +391 -106
  4. openai_sdk_helpers/agent/config.py +405 -44
  5. openai_sdk_helpers/agent/coordination.py +68 -31
  6. openai_sdk_helpers/agent/runner.py +29 -19
  7. openai_sdk_helpers/agent/search/base.py +103 -54
  8. openai_sdk_helpers/agent/search/vector.py +99 -68
  9. openai_sdk_helpers/agent/search/web.py +84 -50
  10. openai_sdk_helpers/agent/summarizer.py +33 -7
  11. openai_sdk_helpers/agent/translator.py +58 -24
  12. openai_sdk_helpers/agent/validation.py +35 -4
  13. openai_sdk_helpers/cli.py +42 -0
  14. openai_sdk_helpers/config.py +0 -1
  15. openai_sdk_helpers/environment.py +3 -2
  16. openai_sdk_helpers/files_api.py +35 -3
  17. openai_sdk_helpers/prompt/base.py +6 -0
  18. openai_sdk_helpers/response/__init__.py +3 -3
  19. openai_sdk_helpers/response/base.py +161 -22
  20. openai_sdk_helpers/response/config.py +50 -200
  21. openai_sdk_helpers/response/files.py +5 -5
  22. openai_sdk_helpers/response/messages.py +3 -3
  23. openai_sdk_helpers/response/runner.py +7 -7
  24. openai_sdk_helpers/response/tool_call.py +94 -4
  25. openai_sdk_helpers/response/vector_store.py +3 -3
  26. openai_sdk_helpers/streamlit_app/app.py +16 -16
  27. openai_sdk_helpers/streamlit_app/config.py +38 -37
  28. openai_sdk_helpers/streamlit_app/streamlit_web_search.py +2 -2
  29. openai_sdk_helpers/structure/__init__.py +6 -2
  30. openai_sdk_helpers/structure/agent_blueprint.py +2 -2
  31. openai_sdk_helpers/structure/base.py +8 -99
  32. openai_sdk_helpers/structure/plan/plan.py +2 -2
  33. openai_sdk_helpers/structure/plan/task.py +9 -9
  34. openai_sdk_helpers/structure/prompt.py +2 -2
  35. openai_sdk_helpers/structure/responses.py +15 -15
  36. openai_sdk_helpers/structure/summary.py +3 -3
  37. openai_sdk_helpers/structure/translation.py +32 -0
  38. openai_sdk_helpers/structure/validation.py +2 -2
  39. openai_sdk_helpers/structure/vector_search.py +7 -7
  40. openai_sdk_helpers/structure/web_search.py +6 -6
  41. openai_sdk_helpers/tools.py +41 -15
  42. openai_sdk_helpers/utils/__init__.py +19 -5
  43. openai_sdk_helpers/utils/instructions.py +35 -0
  44. openai_sdk_helpers/utils/json/__init__.py +55 -0
  45. openai_sdk_helpers/utils/json/base_model.py +181 -0
  46. openai_sdk_helpers/utils/{json_utils.py → json/data_class.py} +43 -70
  47. openai_sdk_helpers/utils/json/ref.py +113 -0
  48. openai_sdk_helpers/utils/json/utils.py +203 -0
  49. openai_sdk_helpers/utils/output_validation.py +21 -1
  50. openai_sdk_helpers/utils/path_utils.py +34 -1
  51. openai_sdk_helpers/utils/registry.py +194 -0
  52. openai_sdk_helpers/vector_storage/storage.py +10 -0
  53. {openai_sdk_helpers-0.2.0.dist-info → openai_sdk_helpers-0.4.0.dist-info}/METADATA +7 -7
  54. openai_sdk_helpers-0.4.0.dist-info/RECORD +86 -0
  55. openai_sdk_helpers-0.2.0.dist-info/RECORD +0 -79
  56. {openai_sdk_helpers-0.2.0.dist-info → openai_sdk_helpers-0.4.0.dist-info}/WHEEL +0 -0
  57. {openai_sdk_helpers-0.2.0.dist-info → openai_sdk_helpers-0.4.0.dist-info}/entry_points.txt +0 -0
  58. {openai_sdk_helpers-0.2.0.dist-info → openai_sdk_helpers-0.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,181 @@
1
+ """Pydantic BaseModel JSON serialization support.
2
+
3
+ This module provides BaseModelJSONSerializable for Pydantic models,
4
+ with to_json, to_json_file, from_json, from_json_file methods and
5
+ customizable _serialize_fields/_deserialize_fields hooks.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ from pathlib import Path
12
+ from typing import Any, TypeVar
13
+ from pydantic import BaseModel
14
+ from ..path_utils import check_filepath
15
+ from .utils import _to_jsonable, customJSONEncoder
16
+
17
+ P = TypeVar("P", bound="BaseModelJSONSerializable")
18
+
19
+
20
+ class BaseModelJSONSerializable(BaseModel):
21
+ """Pydantic BaseModel subclass with JSON serialization support.
22
+
23
+ Adds to_json(), to_json_file(path), from_json(data), from_json_file(path),
24
+ plus overridable _serialize_fields(data) and _deserialize_fields(data) hooks.
25
+
26
+ Methods
27
+ -------
28
+ to_json()
29
+ Return a JSON-compatible dict representation.
30
+ to_json_file(filepath)
31
+ Write serialized JSON data to a file path.
32
+ from_json(data)
33
+ Create an instance from a JSON-compatible dict (class method).
34
+ from_json_file(filepath)
35
+ Load an instance from a JSON file (class method).
36
+ _serialize_fields(data)
37
+ Customize serialization (override in subclasses).
38
+ _deserialize_fields(data)
39
+ Customize deserialization (override in subclasses).
40
+
41
+ Examples
42
+ --------
43
+ >>> from pydantic import BaseModel
44
+ >>> class MyConfig(BaseModelJSONSerializable, BaseModel):
45
+ ... name: str
46
+ ... value: int
47
+ >>> cfg = MyConfig(name="test", value=42)
48
+ >>> cfg.to_json()
49
+ {'name': 'test', 'value': 42}
50
+ """
51
+
52
+ def to_json(self) -> dict[str, Any]:
53
+ """Return a JSON-compatible dict representation.
54
+
55
+ Returns
56
+ -------
57
+ dict[str, Any]
58
+ Serialized model data.
59
+ """
60
+ if hasattr(self, "model_dump"):
61
+ data = getattr(self, "model_dump")()
62
+ else:
63
+ data = self.__dict__.copy()
64
+ return self._serialize_fields(_to_jsonable(data))
65
+
66
+ def to_json_file(self, filepath: str | Path) -> str:
67
+ """Write serialized JSON data to a file path.
68
+
69
+ Parameters
70
+ ----------
71
+ filepath : str or Path
72
+ Path where the JSON file will be written.
73
+
74
+ Returns
75
+ -------
76
+ str
77
+ Absolute path to the written file.
78
+ """
79
+ target = Path(filepath)
80
+ check_filepath(fullfilepath=str(target))
81
+ with open(target, "w", encoding="utf-8") as handle:
82
+ json.dump(
83
+ self.to_json(),
84
+ handle,
85
+ indent=2,
86
+ ensure_ascii=False,
87
+ cls=customJSONEncoder,
88
+ )
89
+ return str(target)
90
+
91
+ def _serialize_fields(self, data: dict[str, Any]) -> dict[str, Any]:
92
+ """Customize field serialization.
93
+
94
+ Override this method in subclasses to add custom serialization logic.
95
+
96
+ Parameters
97
+ ----------
98
+ data : dict[str, Any]
99
+ Pre-serialized data dictionary.
100
+
101
+ Returns
102
+ -------
103
+ dict[str, Any]
104
+ Modified data dictionary.
105
+ """
106
+ return data
107
+
108
+ @classmethod
109
+ def _deserialize_fields(cls, data: dict[str, Any]) -> dict[str, Any]:
110
+ """Customize field deserialization.
111
+
112
+ Override this method in subclasses to add custom deserialization logic.
113
+
114
+ Parameters
115
+ ----------
116
+ data : dict[str, Any]
117
+ Raw data dictionary from JSON.
118
+
119
+ Returns
120
+ -------
121
+ dict[str, Any]
122
+ Modified data dictionary.
123
+ """
124
+ return data
125
+
126
+ @classmethod
127
+ def from_json(cls: type[P], data: dict[str, Any]) -> P:
128
+ """Create an instance from a JSON-compatible dict.
129
+
130
+ Parameters
131
+ ----------
132
+ data : dict[str, Any]
133
+ JSON-compatible dictionary containing the instance data.
134
+
135
+ Returns
136
+ -------
137
+ P
138
+ New instance of the class.
139
+
140
+ Examples
141
+ --------
142
+ >>> json_data = {"name": "test", "value": 42}
143
+ >>> instance = MyConfig.from_json(json_data)
144
+ """
145
+ processed_data = cls._deserialize_fields(data)
146
+ return cls(**processed_data) # type: ignore[return-value]
147
+
148
+ @classmethod
149
+ def from_json_file(cls: type[P], filepath: str | Path) -> P:
150
+ """Load an instance from a JSON file.
151
+
152
+ Parameters
153
+ ----------
154
+ filepath : str or Path
155
+ Path to the JSON file to load.
156
+
157
+ Returns
158
+ -------
159
+ P
160
+ New instance of the class loaded from the file.
161
+
162
+ Raises
163
+ ------
164
+ FileNotFoundError
165
+ If the file does not exist.
166
+
167
+ Examples
168
+ --------
169
+ >>> instance = MyConfig.from_json_file("config.json")
170
+ """
171
+ target = Path(filepath)
172
+ if not target.exists():
173
+ raise FileNotFoundError(f"JSON file not found: {target}")
174
+
175
+ with open(target, "r", encoding="utf-8") as handle:
176
+ data = json.load(handle)
177
+
178
+ return cls.from_json(data)
179
+
180
+
181
+ __all__ = ["BaseModelJSONSerializable"]
@@ -1,73 +1,24 @@
1
- """JSON serialization helpers for helper types."""
1
+ """Dataclass JSON serialization mixin.
2
+
3
+ This module provides the DataclassJSONSerializable mixin for dataclasses,
4
+ adding to_json, to_json_file, from_json, and from_json_file methods.
5
+ """
2
6
 
3
7
  from __future__ import annotations
4
8
 
5
9
  import json
6
10
  from dataclasses import asdict, fields, is_dataclass
7
- from datetime import datetime
8
- from enum import Enum
9
11
  from pathlib import Path
10
12
  from typing import Any, TypeVar, Union, get_args, get_origin, get_type_hints
11
13
 
12
- from .path_utils import check_filepath
13
-
14
- T = TypeVar("T", bound="JSONSerializable")
15
-
16
-
17
- def _to_jsonable(value: Any) -> Any:
18
- """Convert common helper types to JSON-serializable forms."""
19
- from openai_sdk_helpers.structure.base import BaseStructure
20
-
21
- if value is None:
22
- return None
23
- if isinstance(value, Enum):
24
- return value.value
25
- if isinstance(value, Path):
26
- return str(value)
27
- if isinstance(value, datetime):
28
- return value.isoformat()
29
- if is_dataclass(value) and not isinstance(value, type):
30
- return {k: _to_jsonable(v) for k, v in asdict(value).items()}
31
- if hasattr(value, "model_dump"):
32
- model_dump = getattr(value, "model_dump")
33
- return model_dump()
34
- if isinstance(value, dict):
35
- return {str(k): _to_jsonable(v) for k, v in value.items()}
36
- if isinstance(value, (list, tuple, set)):
37
- return [_to_jsonable(v) for v in value]
38
- if isinstance(value, BaseStructure):
39
- return value.model_dump()
40
- return value
41
-
42
-
43
- def coerce_jsonable(value: Any) -> Any:
44
- """Convert value into a JSON-serializable representation."""
45
- from openai_sdk_helpers.response.base import BaseResponse
46
-
47
- if value is None:
48
- return None
49
- if isinstance(value, BaseResponse):
50
- return coerce_jsonable(value.messages.to_json())
51
- if is_dataclass(value) and not isinstance(value, type):
52
- return {key: coerce_jsonable(item) for key, item in asdict(value).items()}
53
- coerced = _to_jsonable(value)
54
- try:
55
- json.dumps(coerced)
56
- return coerced
57
- except TypeError:
58
- return str(coerced)
59
-
60
-
61
- class customJSONEncoder(json.JSONEncoder):
62
- """JSON encoder for common helper types like enums and paths."""
63
-
64
- def default(self, o: Any) -> Any: # noqa: D401
65
- """Return JSON-serializable representation of ``o``."""
66
- return _to_jsonable(o)
67
-
68
-
69
- class JSONSerializable:
70
- """Mixin for classes that can be serialized to and from JSON.
14
+ from ..path_utils import check_filepath
15
+ from .utils import _to_jsonable, customJSONEncoder
16
+
17
+ T = TypeVar("T", bound="DataclassJSONSerializable")
18
+
19
+
20
+ class DataclassJSONSerializable:
21
+ """Mixin for dataclasses that can be serialized to and from JSON.
71
22
 
72
23
  Methods
73
24
  -------
@@ -79,10 +30,28 @@ class JSONSerializable:
79
30
  Create an instance from a JSON-compatible dict (class method).
80
31
  from_json_file(filepath)
81
32
  Load an instance from a JSON file (class method).
33
+
34
+ Examples
35
+ --------
36
+ >>> from dataclasses import dataclass
37
+ >>> from pathlib import Path
38
+ >>> @dataclass
39
+ ... class MyData(DataclassJSONSerializable):
40
+ ... name: str
41
+ ... path: Path
42
+ >>> instance = MyData(name="test", path=Path("/tmp/data"))
43
+ >>> json_data = instance.to_json()
44
+ >>> restored = MyData.from_json(json_data)
82
45
  """
83
46
 
84
47
  def to_json(self) -> dict[str, Any]:
85
- """Return a JSON-compatible dict representation."""
48
+ """Return a JSON-compatible dict representation.
49
+
50
+ Returns
51
+ -------
52
+ dict[str, Any]
53
+ Serialized data dictionary.
54
+ """
86
55
  if is_dataclass(self) and not isinstance(self, type):
87
56
  return {k: _to_jsonable(v) for k, v in asdict(self).items()}
88
57
  if hasattr(self, "model_dump"):
@@ -161,9 +130,17 @@ class JSONSerializable:
161
130
  origin = get_origin(field_type)
162
131
  if origin is Union:
163
132
  type_args = get_args(field_type)
164
- # Check if Path is one of the union types
133
+ # Only convert to Path if:
134
+ # 1. Path is in the union AND
135
+ # 2. str is NOT in the union (to avoid converting string fields)
136
+ # OR the field name suggests it's a path (contains "path")
165
137
  if Path in type_args:
166
- should_convert_to_path = True
138
+ if str not in type_args:
139
+ # Path-only union (e.g., Union[Path, None])
140
+ should_convert_to_path = True
141
+ elif "path" in key.lower():
142
+ # Field name contains "path", likely meant to be a path
143
+ should_convert_to_path = True
167
144
 
168
145
  # Convert string to Path if needed
169
146
  if (
@@ -215,8 +192,4 @@ class JSONSerializable:
215
192
  return cls.from_json(data)
216
193
 
217
194
 
218
- __all__ = [
219
- "coerce_jsonable",
220
- "JSONSerializable",
221
- "customJSONEncoder",
222
- ]
195
+ __all__ = ["DataclassJSONSerializable"]
@@ -0,0 +1,113 @@
1
+ """Reference encoding helpers for object reconstruction.
2
+
3
+ This module provides helpers for encoding and decoding object references
4
+ using module and qualname information, enabling serialization of class
5
+ references for later reconstruction.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import importlib
11
+ from typing import Any
12
+
13
+
14
+ def get_module_qualname(obj: Any) -> tuple[str, str] | None:
15
+ """Retrieve module and qualname for an object.
16
+
17
+ Safe retrieval that returns None if module or qualname cannot be determined.
18
+
19
+ Parameters
20
+ ----------
21
+ obj : Any
22
+ Object to get module and qualname from.
23
+
24
+ Returns
25
+ -------
26
+ tuple[str, str] or None
27
+ Tuple of (module, qualname) or None if cannot be determined.
28
+
29
+ Examples
30
+ --------
31
+ >>> class MyClass:
32
+ ... pass
33
+ >>> get_module_qualname(MyClass)
34
+ ('__main__', 'MyClass')
35
+ """
36
+ module = getattr(obj, "__module__", None)
37
+ qualname = getattr(obj, "__qualname__", None)
38
+ if module and qualname:
39
+ return (module, qualname)
40
+ return None
41
+
42
+
43
+ def encode_module_qualname(obj: Any) -> dict[str, Any] | None:
44
+ """Encode object reference for import reconstruction.
45
+
46
+ Parameters
47
+ ----------
48
+ obj : Any
49
+ Object to encode (typically a class).
50
+
51
+ Returns
52
+ -------
53
+ dict[str, Any] or None
54
+ Dictionary with 'module' and 'qualname' keys, or None if encoding fails.
55
+
56
+ Examples
57
+ --------
58
+ >>> class MyClass:
59
+ ... pass
60
+ >>> encode_module_qualname(MyClass)
61
+ {'module': '__main__', 'qualname': 'MyClass'}
62
+ """
63
+ result = get_module_qualname(obj)
64
+ if result is None:
65
+ return None
66
+ module, qualname = result
67
+ return {"module": module, "qualname": qualname}
68
+
69
+
70
+ def decode_module_qualname(ref: dict[str, Any]) -> Any | None:
71
+ """Import and retrieve object by encoded reference.
72
+
73
+ Parameters
74
+ ----------
75
+ ref : dict[str, Any]
76
+ Dictionary with 'module' and 'qualname' keys.
77
+
78
+ Returns
79
+ -------
80
+ Any or None
81
+ Retrieved object or None if import/retrieval fails.
82
+
83
+ Examples
84
+ --------
85
+ >>> ref = {'module': 'pathlib', 'qualname': 'Path'}
86
+ >>> decode_module_qualname(ref)
87
+ <class 'pathlib.Path'>
88
+ """
89
+ if not isinstance(ref, dict):
90
+ return None
91
+
92
+ module_name = ref.get("module")
93
+ qualname = ref.get("qualname")
94
+
95
+ if not module_name or not qualname:
96
+ return None
97
+
98
+ try:
99
+ module = importlib.import_module(module_name)
100
+ # Handle nested qualnames (e.g., "OuterClass.InnerClass")
101
+ obj = module
102
+ for attr in qualname.split("."):
103
+ obj = getattr(obj, attr)
104
+ return obj
105
+ except (ImportError, AttributeError):
106
+ return None
107
+
108
+
109
+ __all__ = [
110
+ "get_module_qualname",
111
+ "encode_module_qualname",
112
+ "decode_module_qualname",
113
+ ]
@@ -0,0 +1,203 @@
1
+ """Core JSON serialization utilities.
2
+
3
+ This module provides the core functions for converting common types to
4
+ JSON-serializable forms, including to_jsonable, coerce_jsonable, and
5
+ customJSONEncoder.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ from dataclasses import asdict, is_dataclass
12
+ from datetime import datetime
13
+ from enum import Enum
14
+ from pathlib import Path
15
+ from typing import Any
16
+
17
+ from .ref import encode_module_qualname
18
+
19
+
20
+ def to_jsonable(value: Any) -> Any:
21
+ """Convert common types to JSON-safe forms.
22
+
23
+ Recursively converts containers, dicts, dataclasses, Pydantic models, enums,
24
+ paths, datetimes, and StructureBase instances/classes to JSON-serializable forms.
25
+ Private properties (starting with underscore) are excluded from serialization.
26
+
27
+ Parameters
28
+ ----------
29
+ value : Any
30
+ Value to convert to JSON-serializable form.
31
+
32
+ Returns
33
+ -------
34
+ Any
35
+ JSON-serializable representation of the value.
36
+
37
+ Notes
38
+ -----
39
+ Serialization rules:
40
+ - Enums: use enum.value
41
+ - Paths: serialize to string
42
+ - Datetimes: ISO8601 datetime.isoformat()
43
+ - Dataclasses (instances): asdict followed by recursive conversion
44
+ - Pydantic-like objects: use model_dump() if available
45
+ - Dicts/containers: recursively convert values; dict keys coerced to str
46
+ - Private properties: keys starting with underscore are excluded
47
+ - StructureBase instances: use .model_dump()
48
+ - StructureBase classes: encode with {module, qualname, "__structure_class__": True}
49
+ - Sets: converted to lists
50
+
51
+ Examples
52
+ --------
53
+ >>> from enum import Enum
54
+ >>> class Color(Enum):
55
+ ... RED = "red"
56
+ >>> to_jsonable(Color.RED)
57
+ 'red'
58
+ >>> to_jsonable(Path("/tmp/test"))
59
+ '/tmp/test'
60
+ >>> to_jsonable({"public": 1, "_private": 2})
61
+ {'public': 1}
62
+ """
63
+ return _to_jsonable(value)
64
+
65
+
66
+ def _to_jsonable(value: Any) -> Any:
67
+ """Convert common helper types to JSON-serializable forms (internal)."""
68
+ from openai_sdk_helpers.structure.base import StructureBase
69
+
70
+ if value is None:
71
+ return None
72
+ if isinstance(value, Enum):
73
+ return value.value
74
+ if isinstance(value, Path):
75
+ return str(value)
76
+ if isinstance(value, datetime):
77
+ return value.isoformat()
78
+ if is_dataclass(value) and not isinstance(value, type):
79
+ return {
80
+ k: _to_jsonable(v)
81
+ for k, v in asdict(value).items()
82
+ if not str(k).startswith("_")
83
+ }
84
+ # Check for StructureBase class (not instance) before model_dump check
85
+ if isinstance(value, type):
86
+ try:
87
+ if issubclass(value, StructureBase):
88
+ encoded = encode_module_qualname(value)
89
+ if encoded:
90
+ encoded["__structure_class__"] = True
91
+ return encoded
92
+ return str(value)
93
+ except TypeError:
94
+ # Some type-like objects may pass isinstance(value, type) but
95
+ # still not be valid arguments to issubclass; ignore these.
96
+ pass
97
+ if isinstance(value, StructureBase):
98
+ return value.model_dump()
99
+ # Check for model_dump on instances (after class checks)
100
+ if hasattr(value, "model_dump") and not isinstance(value, type):
101
+ model_dump = getattr(value, "model_dump")
102
+ return model_dump()
103
+ if isinstance(value, dict):
104
+ return {
105
+ str(k): _to_jsonable(v)
106
+ for k, v in value.items()
107
+ if not str(k).startswith("_")
108
+ }
109
+ if isinstance(value, (list, tuple, set)):
110
+ return [_to_jsonable(v) for v in value]
111
+ return value
112
+
113
+
114
+ def coerce_jsonable(value: Any) -> Any:
115
+ """Ensure json.dumps succeeds.
116
+
117
+ Falls back to str when necessary. Special-cases ResponseBase.
118
+
119
+ Parameters
120
+ ----------
121
+ value : Any
122
+ Value to coerce to JSON-serializable form.
123
+
124
+ Returns
125
+ -------
126
+ Any
127
+ JSON-serializable representation, or str(value) as fallback.
128
+
129
+ Notes
130
+ -----
131
+ This function first attempts to convert the value using to_jsonable(),
132
+ then validates it can be serialized with json.dumps(). If serialization
133
+ fails, it falls back to str(value).
134
+
135
+ Special handling for ResponseBase: serialized as messages.to_json().
136
+
137
+ Examples
138
+ --------
139
+ >>> coerce_jsonable({"key": "value"})
140
+ {'key': 'value'}
141
+ >>> class CustomObj:
142
+ ... def __str__(self):
143
+ ... return "custom"
144
+ >>> coerce_jsonable(CustomObj())
145
+ 'custom'
146
+ """
147
+ from openai_sdk_helpers.response.base import ResponseBase
148
+
149
+ if value is None:
150
+ return None
151
+ if isinstance(value, ResponseBase):
152
+ return coerce_jsonable(value.messages.to_json())
153
+ if is_dataclass(value) and not isinstance(value, type):
154
+ return {
155
+ key: coerce_jsonable(item)
156
+ for key, item in asdict(value).items()
157
+ if not (isinstance(key, str) and key.startswith("_"))
158
+ }
159
+ coerced = _to_jsonable(value)
160
+ try:
161
+ json.dumps(coerced)
162
+ return coerced
163
+ except TypeError:
164
+ return str(coerced)
165
+
166
+
167
+ class customJSONEncoder(json.JSONEncoder):
168
+ """JSON encoder delegating to to_jsonable.
169
+
170
+ This encoder handles common types like Enum, Path, datetime, dataclasses,
171
+ sets, StructureBase instances/classes, and Pydantic-like objects.
172
+
173
+ Examples
174
+ --------
175
+ >>> import json
176
+ >>> from enum import Enum
177
+ >>> class Color(Enum):
178
+ ... RED = "red"
179
+ >>> json.dumps({"color": Color.RED}, cls=customJSONEncoder)
180
+ '{"color": "red"}'
181
+ """
182
+
183
+ def default(self, o: Any) -> Any:
184
+ """Return JSON-serializable representation of object.
185
+
186
+ Parameters
187
+ ----------
188
+ o : Any
189
+ Object to serialize.
190
+
191
+ Returns
192
+ -------
193
+ Any
194
+ JSON-serializable representation.
195
+ """
196
+ return _to_jsonable(o)
197
+
198
+
199
+ __all__ = [
200
+ "to_jsonable",
201
+ "coerce_jsonable",
202
+ "customJSONEncoder",
203
+ ]
@@ -82,7 +82,7 @@ class JSONSchemaValidator(ValidationRule):
82
82
  Parameters
83
83
  ----------
84
84
  schema : dict
85
- JSON schema to validate against.
85
+ A dictionary representing the JSON schema to validate against.
86
86
 
87
87
  Examples
88
88
  --------
@@ -100,6 +100,11 @@ class JSONSchemaValidator(ValidationRule):
100
100
  ----------
101
101
  schema : dict
102
102
  JSON schema dictionary.
103
+
104
+ Raises
105
+ ------
106
+ ValueError
107
+ If the schema is not a valid dictionary.
103
108
  """
104
109
  self.schema = schema
105
110
 
@@ -186,6 +191,11 @@ class SemanticValidator(ValidationRule):
186
191
  Phrases that must not appear.
187
192
  must_reference_sources : bool
188
193
  Check for source references.
194
+
195
+ Raises
196
+ ------
197
+ ValueError
198
+ If any of the parameters are not of the expected type.
189
199
  """
190
200
  self.must_contain = must_contain or []
191
201
  self.must_not_contain = must_not_contain or []
@@ -281,6 +291,11 @@ class LengthValidator(ValidationRule):
281
291
  Minimum word count.
282
292
  max_words : int, optional
283
293
  Maximum word count.
294
+
295
+ Raises
296
+ ------
297
+ ValueError
298
+ If any of the parameters are not integers.
284
299
  """
285
300
  self.min_length = min_length
286
301
  self.max_length = max_length
@@ -354,6 +369,11 @@ class OutputValidator:
354
369
  ----------
355
370
  rules : list[ValidationRule], optional
356
371
  Initial validation rules.
372
+
373
+ Raises
374
+ ------
375
+ ValueError
376
+ If rules is not a list of ValidationRule instances.
357
377
  """
358
378
  self.rules = rules or []
359
379