openai-sdk-helpers 0.1.0__py3-none-any.whl → 0.1.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.
Files changed (44) hide show
  1. openai_sdk_helpers/__init__.py +44 -7
  2. openai_sdk_helpers/agent/base.py +5 -1
  3. openai_sdk_helpers/agent/coordination.py +4 -5
  4. openai_sdk_helpers/agent/runner.py +4 -1
  5. openai_sdk_helpers/agent/search/base.py +1 -0
  6. openai_sdk_helpers/agent/search/vector.py +2 -0
  7. openai_sdk_helpers/cli.py +265 -0
  8. openai_sdk_helpers/config.py +93 -2
  9. openai_sdk_helpers/context_manager.py +1 -1
  10. openai_sdk_helpers/deprecation.py +167 -0
  11. openai_sdk_helpers/environment.py +3 -2
  12. openai_sdk_helpers/errors.py +0 -12
  13. openai_sdk_helpers/files_api.py +373 -0
  14. openai_sdk_helpers/logging_config.py +24 -95
  15. openai_sdk_helpers/prompt/base.py +1 -1
  16. openai_sdk_helpers/response/__init__.py +7 -3
  17. openai_sdk_helpers/response/base.py +217 -147
  18. openai_sdk_helpers/response/config.py +16 -1
  19. openai_sdk_helpers/response/files.py +392 -0
  20. openai_sdk_helpers/response/messages.py +1 -0
  21. openai_sdk_helpers/retry.py +1 -1
  22. openai_sdk_helpers/streamlit_app/app.py +97 -7
  23. openai_sdk_helpers/streamlit_app/streamlit_web_search.py +15 -8
  24. openai_sdk_helpers/structure/base.py +6 -6
  25. openai_sdk_helpers/structure/plan/helpers.py +1 -0
  26. openai_sdk_helpers/structure/plan/task.py +7 -7
  27. openai_sdk_helpers/tools.py +116 -13
  28. openai_sdk_helpers/utils/__init__.py +100 -35
  29. openai_sdk_helpers/{async_utils.py → utils/async_utils.py} +5 -6
  30. openai_sdk_helpers/utils/coercion.py +138 -0
  31. openai_sdk_helpers/utils/deprecation.py +167 -0
  32. openai_sdk_helpers/utils/encoding.py +189 -0
  33. openai_sdk_helpers/utils/json_utils.py +98 -0
  34. openai_sdk_helpers/utils/output_validation.py +448 -0
  35. openai_sdk_helpers/utils/path_utils.py +46 -0
  36. openai_sdk_helpers/{validation.py → utils/validation.py} +7 -3
  37. openai_sdk_helpers/vector_storage/storage.py +59 -28
  38. {openai_sdk_helpers-0.1.0.dist-info → openai_sdk_helpers-0.1.2.dist-info}/METADATA +152 -3
  39. openai_sdk_helpers-0.1.2.dist-info/RECORD +79 -0
  40. openai_sdk_helpers-0.1.2.dist-info/entry_points.txt +2 -0
  41. openai_sdk_helpers/utils/core.py +0 -596
  42. openai_sdk_helpers-0.1.0.dist-info/RECORD +0 -69
  43. {openai_sdk_helpers-0.1.0.dist-info → openai_sdk_helpers-0.1.2.dist-info}/WHEEL +0 -0
  44. {openai_sdk_helpers-0.1.0.dist-info → openai_sdk_helpers-0.1.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,189 @@
1
+ """Utilities for encoding files and images to base64.
2
+
3
+ This module provides helper functions for encoding images and files
4
+ to base64 format for use with OpenAI API's input_image and input_file
5
+ content types.
6
+ """
7
+
8
+ import base64
9
+ import mimetypes
10
+ from pathlib import Path
11
+ from typing import Literal
12
+
13
+
14
+ def is_image_file(file_path: str | Path) -> bool:
15
+ """Check if a file is an image based on its MIME type.
16
+
17
+ Parameters
18
+ ----------
19
+ file_path : str or Path
20
+ Path to the file to check.
21
+
22
+ Returns
23
+ -------
24
+ bool
25
+ True if the file is an image, False otherwise.
26
+
27
+ Examples
28
+ --------
29
+ >>> is_image_file("photo.jpg")
30
+ True
31
+ >>> is_image_file("document.pdf")
32
+ False
33
+ """
34
+ mime_type = get_mime_type(file_path)
35
+ return mime_type.startswith("image/")
36
+
37
+
38
+ def encode_image(image_path: str | Path) -> str:
39
+ """Encode an image file to base64.
40
+
41
+ Parameters
42
+ ----------
43
+ image_path : str or Path
44
+ Path to the image file to encode. Relative paths are converted
45
+ to absolute paths.
46
+
47
+ Returns
48
+ -------
49
+ str
50
+ Base64-encoded string representation of the image.
51
+
52
+ Raises
53
+ ------
54
+ FileNotFoundError
55
+ If the image file does not exist.
56
+ IOError
57
+ If the file cannot be read.
58
+
59
+ Examples
60
+ --------
61
+ >>> base64_image = encode_image("photo.jpg")
62
+ >>> image_url = f"data:image/jpeg;base64,{base64_image}"
63
+ """
64
+ path = Path(image_path).resolve()
65
+ if not path.exists():
66
+ raise FileNotFoundError(f"Image file not found: {image_path}")
67
+
68
+ with open(path, "rb") as image_file:
69
+ return base64.b64encode(image_file.read()).decode("utf-8")
70
+
71
+
72
+ def encode_file(file_path: str | Path) -> str:
73
+ """Encode a file to base64.
74
+
75
+ Parameters
76
+ ----------
77
+ file_path : str or Path
78
+ Path to the file to encode. Relative paths are converted
79
+ to absolute paths.
80
+
81
+ Returns
82
+ -------
83
+ str
84
+ Base64-encoded string representation of the file.
85
+
86
+ Raises
87
+ ------
88
+ FileNotFoundError
89
+ If the file does not exist.
90
+ IOError
91
+ If the file cannot be read.
92
+
93
+ Examples
94
+ --------
95
+ >>> base64_file = encode_file("document.pdf")
96
+ >>> file_data = f"data:application/pdf;base64,{base64_file}"
97
+ """
98
+ path = Path(file_path).resolve()
99
+ if not path.exists():
100
+ raise FileNotFoundError(f"File not found: {file_path}")
101
+
102
+ with open(path, "rb") as f:
103
+ return base64.b64encode(f.read()).decode("utf-8")
104
+
105
+
106
+ def get_mime_type(file_path: str | Path) -> str:
107
+ """Get the MIME type of a file.
108
+
109
+ Parameters
110
+ ----------
111
+ file_path : str or Path
112
+ Path to the file.
113
+
114
+ Returns
115
+ -------
116
+ str
117
+ MIME type of the file, or "application/octet-stream" if unknown.
118
+
119
+ Examples
120
+ --------
121
+ >>> get_mime_type("photo.jpg")
122
+ 'image/jpeg'
123
+ >>> get_mime_type("document.pdf")
124
+ 'application/pdf'
125
+ """
126
+ path = Path(file_path)
127
+ mime_type, _ = mimetypes.guess_type(str(path))
128
+ return mime_type or "application/octet-stream"
129
+
130
+
131
+ def create_image_data_url(
132
+ image_path: str | Path, detail: Literal["low", "high", "auto"] = "auto"
133
+ ) -> tuple[str, Literal["low", "high", "auto"]]:
134
+ """Create a data URL for an image with MIME type detection.
135
+
136
+ Parameters
137
+ ----------
138
+ image_path : str or Path
139
+ Path to the image file.
140
+ detail : {"low", "high", "auto"}, default "auto"
141
+ Detail level for image processing.
142
+
143
+ Returns
144
+ -------
145
+ tuple[str, str]
146
+ A tuple containing the data URL and detail level.
147
+
148
+ Raises
149
+ ------
150
+ FileNotFoundError
151
+ If the image file does not exist.
152
+
153
+ Examples
154
+ --------
155
+ >>> image_url, detail = create_image_data_url("photo.jpg", "high")
156
+ >>> # Use with ResponseInputImageContentParam
157
+ """
158
+ mime_type = get_mime_type(image_path)
159
+ base64_image = encode_image(image_path)
160
+ data_url = f"data:{mime_type};base64,{base64_image}"
161
+ return data_url, detail
162
+
163
+
164
+ def create_file_data_url(file_path: str | Path) -> str:
165
+ """Create a data URL for a file with MIME type detection.
166
+
167
+ Parameters
168
+ ----------
169
+ file_path : str or Path
170
+ Path to the file.
171
+
172
+ Returns
173
+ -------
174
+ str
175
+ Data URL with MIME type and base64-encoded content.
176
+
177
+ Raises
178
+ ------
179
+ FileNotFoundError
180
+ If the file does not exist.
181
+
182
+ Examples
183
+ --------
184
+ >>> file_data = create_file_data_url("document.pdf")
185
+ >>> # Use with ResponseInputFileContentParam
186
+ """
187
+ mime_type = get_mime_type(file_path)
188
+ base64_file = encode_file(file_path)
189
+ return f"data:{mime_type};base64,{base64_file}"
@@ -0,0 +1,98 @@
1
+ """JSON serialization helpers for helper types."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from dataclasses import asdict, is_dataclass
7
+ from datetime import datetime
8
+ from enum import Enum
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+ from .path_utils import check_filepath
13
+
14
+
15
+ def _to_jsonable(value: Any) -> Any:
16
+ """Convert common helper types to JSON-serializable forms."""
17
+ from openai_sdk_helpers.structure.base import BaseStructure
18
+
19
+ if value is None:
20
+ return None
21
+ if isinstance(value, Enum):
22
+ return value.value
23
+ if isinstance(value, Path):
24
+ return str(value)
25
+ if isinstance(value, datetime):
26
+ return value.isoformat()
27
+ if is_dataclass(value) and not isinstance(value, type):
28
+ return {k: _to_jsonable(v) for k, v in asdict(value).items()}
29
+ if hasattr(value, "model_dump"):
30
+ model_dump = getattr(value, "model_dump")
31
+ return model_dump()
32
+ if isinstance(value, dict):
33
+ return {str(k): _to_jsonable(v) for k, v in value.items()}
34
+ if isinstance(value, (list, tuple, set)):
35
+ return [_to_jsonable(v) for v in value]
36
+ if isinstance(value, BaseStructure):
37
+ return value.model_dump()
38
+ return value
39
+
40
+
41
+ def coerce_jsonable(value: Any) -> Any:
42
+ """Convert value into a JSON-serializable representation."""
43
+ from openai_sdk_helpers.response.base import BaseResponse
44
+
45
+ if value is None:
46
+ return None
47
+ if isinstance(value, BaseResponse):
48
+ return coerce_jsonable(value.messages.to_json())
49
+ if is_dataclass(value) and not isinstance(value, type):
50
+ return {key: coerce_jsonable(item) for key, item in asdict(value).items()}
51
+ coerced = _to_jsonable(value)
52
+ try:
53
+ json.dumps(coerced)
54
+ return coerced
55
+ except TypeError:
56
+ return str(coerced)
57
+
58
+
59
+ class customJSONEncoder(json.JSONEncoder):
60
+ """JSON encoder for common helper types like enums and paths."""
61
+
62
+ def default(self, o: Any) -> Any: # noqa: D401
63
+ """Return JSON-serializable representation of ``o``."""
64
+ return _to_jsonable(o)
65
+
66
+
67
+ class JSONSerializable:
68
+ """Mixin for classes that can be serialized to JSON."""
69
+
70
+ def to_json(self) -> dict[str, Any]:
71
+ """Return a JSON-compatible dict representation."""
72
+ if is_dataclass(self) and not isinstance(self, type):
73
+ return {k: _to_jsonable(v) for k, v in asdict(self).items()}
74
+ if hasattr(self, "model_dump"):
75
+ model_dump = getattr(self, "model_dump")
76
+ return _to_jsonable(model_dump())
77
+ return _to_jsonable(self.__dict__)
78
+
79
+ def to_json_file(self, filepath: str | Path) -> str:
80
+ """Write serialized JSON data to a file path."""
81
+ target = Path(filepath)
82
+ check_filepath(fullfilepath=str(target))
83
+ with open(target, "w", encoding="utf-8") as handle:
84
+ json.dump(
85
+ self.to_json(),
86
+ handle,
87
+ indent=2,
88
+ ensure_ascii=False,
89
+ cls=customJSONEncoder,
90
+ )
91
+ return str(target)
92
+
93
+
94
+ __all__ = [
95
+ "coerce_jsonable",
96
+ "JSONSerializable",
97
+ "customJSONEncoder",
98
+ ]