openai-sdk-helpers 0.0.7__py3-none-any.whl → 0.0.9__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.
- openai_sdk_helpers/__init__.py +85 -10
- openai_sdk_helpers/agent/__init__.py +8 -4
- openai_sdk_helpers/agent/base.py +81 -46
- openai_sdk_helpers/agent/config.py +6 -4
- openai_sdk_helpers/agent/{project_manager.py → coordination.py} +29 -45
- openai_sdk_helpers/agent/prompt_utils.py +7 -1
- openai_sdk_helpers/agent/runner.py +67 -141
- openai_sdk_helpers/agent/search/__init__.py +33 -0
- openai_sdk_helpers/agent/search/base.py +297 -0
- openai_sdk_helpers/agent/{vector_search.py → search/vector.py} +89 -157
- openai_sdk_helpers/agent/{web_search.py → search/web.py} +82 -162
- openai_sdk_helpers/agent/summarizer.py +29 -8
- openai_sdk_helpers/agent/translator.py +40 -13
- openai_sdk_helpers/agent/validation.py +32 -8
- openai_sdk_helpers/async_utils.py +132 -0
- openai_sdk_helpers/config.py +74 -36
- openai_sdk_helpers/context_manager.py +241 -0
- openai_sdk_helpers/enums/__init__.py +9 -1
- openai_sdk_helpers/enums/base.py +67 -8
- openai_sdk_helpers/environment.py +33 -6
- openai_sdk_helpers/errors.py +133 -0
- openai_sdk_helpers/logging_config.py +105 -0
- openai_sdk_helpers/prompt/__init__.py +10 -71
- openai_sdk_helpers/prompt/base.py +172 -0
- openai_sdk_helpers/response/__init__.py +37 -5
- openai_sdk_helpers/response/base.py +427 -189
- openai_sdk_helpers/response/config.py +176 -0
- openai_sdk_helpers/response/messages.py +104 -40
- openai_sdk_helpers/response/runner.py +79 -35
- openai_sdk_helpers/response/tool_call.py +75 -12
- openai_sdk_helpers/response/vector_store.py +29 -16
- openai_sdk_helpers/retry.py +175 -0
- openai_sdk_helpers/streamlit_app/__init__.py +30 -0
- openai_sdk_helpers/streamlit_app/app.py +345 -0
- openai_sdk_helpers/streamlit_app/config.py +502 -0
- openai_sdk_helpers/streamlit_app/streamlit_web_search.py +68 -0
- openai_sdk_helpers/structure/__init__.py +69 -3
- openai_sdk_helpers/structure/agent_blueprint.py +82 -19
- openai_sdk_helpers/structure/base.py +245 -91
- openai_sdk_helpers/structure/plan/__init__.py +15 -1
- openai_sdk_helpers/structure/plan/enum.py +41 -5
- openai_sdk_helpers/structure/plan/plan.py +101 -45
- openai_sdk_helpers/structure/plan/task.py +38 -6
- openai_sdk_helpers/structure/prompt.py +21 -2
- openai_sdk_helpers/structure/responses.py +52 -11
- openai_sdk_helpers/structure/summary.py +55 -7
- openai_sdk_helpers/structure/validation.py +34 -6
- openai_sdk_helpers/structure/vector_search.py +132 -18
- openai_sdk_helpers/structure/web_search.py +128 -12
- openai_sdk_helpers/types.py +57 -0
- openai_sdk_helpers/utils/__init__.py +32 -1
- openai_sdk_helpers/utils/core.py +200 -32
- openai_sdk_helpers/validation.py +302 -0
- openai_sdk_helpers/vector_storage/__init__.py +21 -1
- openai_sdk_helpers/vector_storage/cleanup.py +25 -13
- openai_sdk_helpers/vector_storage/storage.py +124 -66
- openai_sdk_helpers/vector_storage/types.py +20 -19
- openai_sdk_helpers-0.0.9.dist-info/METADATA +550 -0
- openai_sdk_helpers-0.0.9.dist-info/RECORD +66 -0
- openai_sdk_helpers-0.0.7.dist-info/METADATA +0 -193
- openai_sdk_helpers-0.0.7.dist-info/RECORD +0 -51
- {openai_sdk_helpers-0.0.7.dist-info → openai_sdk_helpers-0.0.9.dist-info}/WHEEL +0 -0
- {openai_sdk_helpers-0.0.7.dist-info → openai_sdk_helpers-0.0.9.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
"""Input validation utilities for openai-sdk-helpers.
|
|
2
|
+
|
|
3
|
+
Provides validators and validation helpers for ensuring data integrity
|
|
4
|
+
at API boundaries and configuration initialization.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from collections.abc import Mapping
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Callable, TypeVar
|
|
10
|
+
|
|
11
|
+
from openai_sdk_helpers.errors import InputValidationError
|
|
12
|
+
|
|
13
|
+
T = TypeVar("T")
|
|
14
|
+
K = TypeVar("K", bound=str)
|
|
15
|
+
V = TypeVar("V")
|
|
16
|
+
U = TypeVar("U")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def validate_non_empty_string(value: str, field_name: str) -> str:
|
|
20
|
+
"""Validate that a string is non-empty.
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
value : str
|
|
25
|
+
String value to validate.
|
|
26
|
+
field_name : str
|
|
27
|
+
Name of the field for error messages.
|
|
28
|
+
|
|
29
|
+
Returns
|
|
30
|
+
-------
|
|
31
|
+
str
|
|
32
|
+
The validated (stripped) string.
|
|
33
|
+
|
|
34
|
+
Raises
|
|
35
|
+
------
|
|
36
|
+
InputValidationError
|
|
37
|
+
If string is empty or only whitespace.
|
|
38
|
+
"""
|
|
39
|
+
if not isinstance(value, str):
|
|
40
|
+
raise InputValidationError(
|
|
41
|
+
f"{field_name} must be a string, got {type(value).__name__}"
|
|
42
|
+
)
|
|
43
|
+
stripped = value.strip()
|
|
44
|
+
if not stripped:
|
|
45
|
+
raise InputValidationError(f"{field_name} must be non-empty")
|
|
46
|
+
return stripped
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def validate_max_length(value: str, max_len: int, field_name: str) -> str:
|
|
50
|
+
"""Validate that a string doesn't exceed maximum length.
|
|
51
|
+
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
value : str
|
|
55
|
+
String value to validate.
|
|
56
|
+
max_len : int
|
|
57
|
+
Maximum allowed length.
|
|
58
|
+
field_name : str
|
|
59
|
+
Name of the field for error messages.
|
|
60
|
+
|
|
61
|
+
Returns
|
|
62
|
+
-------
|
|
63
|
+
str
|
|
64
|
+
The validated string.
|
|
65
|
+
|
|
66
|
+
Raises
|
|
67
|
+
------
|
|
68
|
+
InputValidationError
|
|
69
|
+
If string exceeds maximum length.
|
|
70
|
+
"""
|
|
71
|
+
if len(value) > max_len:
|
|
72
|
+
raise InputValidationError(
|
|
73
|
+
f"{field_name} must be <= {max_len} characters, "
|
|
74
|
+
f"got {len(value)} characters"
|
|
75
|
+
)
|
|
76
|
+
return value
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def validate_url_format(url: str, field_name: str = "URL") -> str:
|
|
80
|
+
"""Validate that a string is a valid URL.
|
|
81
|
+
|
|
82
|
+
Parameters
|
|
83
|
+
----------
|
|
84
|
+
url : str
|
|
85
|
+
URL string to validate.
|
|
86
|
+
field_name : str
|
|
87
|
+
Name of the field for error messages. Default is "URL".
|
|
88
|
+
|
|
89
|
+
Returns
|
|
90
|
+
-------
|
|
91
|
+
str
|
|
92
|
+
The validated URL.
|
|
93
|
+
|
|
94
|
+
Raises
|
|
95
|
+
------
|
|
96
|
+
InputValidationError
|
|
97
|
+
If URL format is invalid.
|
|
98
|
+
"""
|
|
99
|
+
if not url.startswith(("http://", "https://")):
|
|
100
|
+
raise InputValidationError(
|
|
101
|
+
f"{field_name} must start with http:// or https://, got: {url}"
|
|
102
|
+
)
|
|
103
|
+
return url
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def validate_dict_mapping(
|
|
107
|
+
mapping: Mapping[K, V],
|
|
108
|
+
expected_keys: set[K],
|
|
109
|
+
field_name: str,
|
|
110
|
+
allow_extra: bool = False,
|
|
111
|
+
) -> dict[K, V]:
|
|
112
|
+
"""Validate that a dict contains expected keys.
|
|
113
|
+
|
|
114
|
+
Parameters
|
|
115
|
+
----------
|
|
116
|
+
mapping : Mapping
|
|
117
|
+
Dictionary-like object to validate.
|
|
118
|
+
expected_keys : set
|
|
119
|
+
Set of required key names.
|
|
120
|
+
field_name : str
|
|
121
|
+
Name of the field for error messages.
|
|
122
|
+
allow_extra : bool
|
|
123
|
+
Whether extra keys are allowed. Default is False.
|
|
124
|
+
|
|
125
|
+
Returns
|
|
126
|
+
-------
|
|
127
|
+
dict
|
|
128
|
+
The validated dictionary.
|
|
129
|
+
|
|
130
|
+
Raises
|
|
131
|
+
------
|
|
132
|
+
InputValidationError
|
|
133
|
+
If required keys are missing or unexpected keys present (if not allowed).
|
|
134
|
+
"""
|
|
135
|
+
if not isinstance(mapping, Mapping):
|
|
136
|
+
raise InputValidationError(
|
|
137
|
+
f"{field_name} must be a dict or mapping, got {type(mapping).__name__}"
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
missing_keys = expected_keys - set(mapping.keys())
|
|
141
|
+
if missing_keys:
|
|
142
|
+
raise InputValidationError(
|
|
143
|
+
f"{field_name} missing required keys: {', '.join(sorted(missing_keys))}"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
if not allow_extra:
|
|
147
|
+
extra_keys = set(mapping.keys()) - expected_keys
|
|
148
|
+
if extra_keys:
|
|
149
|
+
raise InputValidationError(
|
|
150
|
+
f"{field_name} has unexpected keys: {', '.join(sorted(extra_keys))}"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
return dict(mapping)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def validate_list_items(
|
|
157
|
+
items: list[U],
|
|
158
|
+
item_validator: Callable[[U], T],
|
|
159
|
+
field_name: str,
|
|
160
|
+
allow_empty: bool = False,
|
|
161
|
+
) -> list[T]:
|
|
162
|
+
"""Validate all items in a list using a validator function.
|
|
163
|
+
|
|
164
|
+
Parameters
|
|
165
|
+
----------
|
|
166
|
+
items : list
|
|
167
|
+
List to validate.
|
|
168
|
+
item_validator : Callable
|
|
169
|
+
Function that validates individual items.
|
|
170
|
+
field_name : str
|
|
171
|
+
Name of the field for error messages.
|
|
172
|
+
allow_empty : bool
|
|
173
|
+
Whether an empty list is allowed. Default is False.
|
|
174
|
+
|
|
175
|
+
Returns
|
|
176
|
+
-------
|
|
177
|
+
list
|
|
178
|
+
List of validated items.
|
|
179
|
+
|
|
180
|
+
Raises
|
|
181
|
+
------
|
|
182
|
+
InputValidationError
|
|
183
|
+
If list is empty when not allowed, or if any item fails validation.
|
|
184
|
+
"""
|
|
185
|
+
if not isinstance(items, list):
|
|
186
|
+
raise InputValidationError(
|
|
187
|
+
f"{field_name} must be a list, got {type(items).__name__}"
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
if not items and not allow_empty:
|
|
191
|
+
raise InputValidationError(f"{field_name} must not be empty")
|
|
192
|
+
|
|
193
|
+
validated = []
|
|
194
|
+
for i, item in enumerate(items):
|
|
195
|
+
try:
|
|
196
|
+
validated.append(item_validator(item))
|
|
197
|
+
except (InputValidationError, ValueError) as exc:
|
|
198
|
+
raise InputValidationError(f"{field_name}[{i}] is invalid: {exc}") from exc
|
|
199
|
+
|
|
200
|
+
return validated
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def validate_choice(
|
|
204
|
+
value: U,
|
|
205
|
+
allowed_values: set[U],
|
|
206
|
+
field_name: str,
|
|
207
|
+
) -> U:
|
|
208
|
+
"""Validate that a value is one of allowed choices.
|
|
209
|
+
|
|
210
|
+
Parameters
|
|
211
|
+
----------
|
|
212
|
+
value : Any
|
|
213
|
+
Value to validate.
|
|
214
|
+
allowed_values : set
|
|
215
|
+
Set of allowed values.
|
|
216
|
+
field_name : str
|
|
217
|
+
Name of the field for error messages.
|
|
218
|
+
|
|
219
|
+
Returns
|
|
220
|
+
-------
|
|
221
|
+
Any
|
|
222
|
+
The validated value.
|
|
223
|
+
|
|
224
|
+
Raises
|
|
225
|
+
------
|
|
226
|
+
InputValidationError
|
|
227
|
+
If value is not in allowed values.
|
|
228
|
+
"""
|
|
229
|
+
if value not in allowed_values:
|
|
230
|
+
raise InputValidationError(
|
|
231
|
+
f"{field_name} must be one of {', '.join(map(str, sorted(allowed_values, key=str)))}; got: {value}"
|
|
232
|
+
)
|
|
233
|
+
return value
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def validate_safe_path(
|
|
237
|
+
path: Path | str,
|
|
238
|
+
base_dir: Path | None = None,
|
|
239
|
+
field_name: str = "path",
|
|
240
|
+
) -> Path:
|
|
241
|
+
"""Validate that a path is safe and does not escape the base directory.
|
|
242
|
+
|
|
243
|
+
Protects against path traversal attacks by ensuring the resolved path
|
|
244
|
+
is within the base directory when provided.
|
|
245
|
+
|
|
246
|
+
Parameters
|
|
247
|
+
----------
|
|
248
|
+
path : Path or str
|
|
249
|
+
Path to validate. Can be absolute or relative.
|
|
250
|
+
base_dir : Path or None
|
|
251
|
+
Base directory to validate against. If None, only checks for
|
|
252
|
+
suspicious patterns but allows any valid path.
|
|
253
|
+
field_name : str
|
|
254
|
+
Name of the field for error messages. Default is "path".
|
|
255
|
+
|
|
256
|
+
Returns
|
|
257
|
+
-------
|
|
258
|
+
Path
|
|
259
|
+
Validated resolved path.
|
|
260
|
+
|
|
261
|
+
Raises
|
|
262
|
+
------
|
|
263
|
+
InputValidationError
|
|
264
|
+
If path is invalid, contains suspicious patterns, or escapes
|
|
265
|
+
the base directory.
|
|
266
|
+
|
|
267
|
+
Examples
|
|
268
|
+
--------
|
|
269
|
+
>>> from pathlib import Path
|
|
270
|
+
>>> validate_safe_path(Path("./templates/file.txt"), Path("/base"))
|
|
271
|
+
PosixPath('/base/templates/file.txt')
|
|
272
|
+
"""
|
|
273
|
+
if isinstance(path, str):
|
|
274
|
+
path = Path(path)
|
|
275
|
+
|
|
276
|
+
# Check for suspicious patterns
|
|
277
|
+
path_str = str(path)
|
|
278
|
+
if ".." in path.parts:
|
|
279
|
+
raise InputValidationError(
|
|
280
|
+
f"{field_name} contains suspicious '..' pattern: {path_str}"
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
# Resolve to absolute path
|
|
284
|
+
try:
|
|
285
|
+
resolved = path.resolve()
|
|
286
|
+
except (OSError, RuntimeError) as exc:
|
|
287
|
+
raise InputValidationError(
|
|
288
|
+
f"{field_name} cannot be resolved: {path_str}"
|
|
289
|
+
) from exc
|
|
290
|
+
|
|
291
|
+
# If base_dir provided, ensure path is within it
|
|
292
|
+
if base_dir is not None:
|
|
293
|
+
try:
|
|
294
|
+
base_resolved = base_dir.resolve()
|
|
295
|
+
resolved.relative_to(base_resolved)
|
|
296
|
+
except ValueError:
|
|
297
|
+
raise InputValidationError(
|
|
298
|
+
f"{field_name} escapes base directory: {path_str} "
|
|
299
|
+
f"is not within {base_dir}"
|
|
300
|
+
) from None
|
|
301
|
+
|
|
302
|
+
return resolved
|
|
@@ -1,4 +1,24 @@
|
|
|
1
|
-
"""Vector store
|
|
1
|
+
"""Vector store management and utilities.
|
|
2
|
+
|
|
3
|
+
This module provides high-level interfaces for managing OpenAI vector stores,
|
|
4
|
+
including file upload, deletion, search operations, and cleanup utilities.
|
|
5
|
+
|
|
6
|
+
Classes
|
|
7
|
+
-------
|
|
8
|
+
VectorStorage
|
|
9
|
+
Manage an OpenAI vector store with file operations and search.
|
|
10
|
+
VectorStorageFileInfo
|
|
11
|
+
Information about a single file in a vector store.
|
|
12
|
+
VectorStorageFileStats
|
|
13
|
+
Aggregate statistics for batch file operations.
|
|
14
|
+
|
|
15
|
+
Functions
|
|
16
|
+
---------
|
|
17
|
+
_delete_all_vector_stores
|
|
18
|
+
Delete all vector stores and clean up orphaned files.
|
|
19
|
+
_delete_all_files
|
|
20
|
+
Delete all files from the OpenAI account.
|
|
21
|
+
"""
|
|
2
22
|
|
|
3
23
|
from __future__ import annotations
|
|
4
24
|
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
"""Cleanup
|
|
1
|
+
"""Cleanup utilities for vector stores.
|
|
2
|
+
|
|
3
|
+
This module provides destructive operations for cleaning up OpenAI vector
|
|
4
|
+
stores and files. Use these functions with caution as they perform
|
|
5
|
+
irreversible deletions.
|
|
6
|
+
"""
|
|
2
7
|
|
|
3
8
|
from __future__ import annotations
|
|
4
9
|
|
|
@@ -12,13 +17,17 @@ from ..utils import log
|
|
|
12
17
|
def _delete_all_vector_stores() -> None:
|
|
13
18
|
"""Delete all vector stores and clean up any orphaned files.
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
Iterates over every vector store in the account, deleting all files
|
|
21
|
+
within each store before removing the store itself. After all stores
|
|
22
|
+
are deleted, removes any remaining orphaned files.
|
|
23
|
+
|
|
24
|
+
Warning: This operation is irreversible and will delete all vector
|
|
25
|
+
stores and files in the account.
|
|
18
26
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
Examples
|
|
28
|
+
--------
|
|
29
|
+
>>> from openai_sdk_helpers.vector_storage import _delete_all_vector_stores
|
|
30
|
+
>>> _delete_all_vector_stores() # doctest: +SKIP
|
|
22
31
|
"""
|
|
23
32
|
try:
|
|
24
33
|
client = OpenAI()
|
|
@@ -73,13 +82,16 @@ def _delete_all_vector_stores() -> None:
|
|
|
73
82
|
def _delete_all_files() -> None:
|
|
74
83
|
"""Delete all files from the OpenAI account.
|
|
75
84
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
85
|
+
Iterates over every file in the account and deletes them without
|
|
86
|
+
checking vector store associations. Use with extreme caution.
|
|
87
|
+
|
|
88
|
+
Warning: This operation is irreversible and will delete all files
|
|
89
|
+
in the account, regardless of their usage in vector stores.
|
|
79
90
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
91
|
+
Examples
|
|
92
|
+
--------
|
|
93
|
+
>>> from openai_sdk_helpers.vector_storage import _delete_all_files
|
|
94
|
+
>>> _delete_all_files() # doctest: +SKIP
|
|
83
95
|
"""
|
|
84
96
|
client = OpenAI()
|
|
85
97
|
all_files = client.files.list().data
|