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.
Files changed (63) hide show
  1. openai_sdk_helpers/__init__.py +85 -10
  2. openai_sdk_helpers/agent/__init__.py +8 -4
  3. openai_sdk_helpers/agent/base.py +81 -46
  4. openai_sdk_helpers/agent/config.py +6 -4
  5. openai_sdk_helpers/agent/{project_manager.py → coordination.py} +29 -45
  6. openai_sdk_helpers/agent/prompt_utils.py +7 -1
  7. openai_sdk_helpers/agent/runner.py +67 -141
  8. openai_sdk_helpers/agent/search/__init__.py +33 -0
  9. openai_sdk_helpers/agent/search/base.py +297 -0
  10. openai_sdk_helpers/agent/{vector_search.py → search/vector.py} +89 -157
  11. openai_sdk_helpers/agent/{web_search.py → search/web.py} +82 -162
  12. openai_sdk_helpers/agent/summarizer.py +29 -8
  13. openai_sdk_helpers/agent/translator.py +40 -13
  14. openai_sdk_helpers/agent/validation.py +32 -8
  15. openai_sdk_helpers/async_utils.py +132 -0
  16. openai_sdk_helpers/config.py +74 -36
  17. openai_sdk_helpers/context_manager.py +241 -0
  18. openai_sdk_helpers/enums/__init__.py +9 -1
  19. openai_sdk_helpers/enums/base.py +67 -8
  20. openai_sdk_helpers/environment.py +33 -6
  21. openai_sdk_helpers/errors.py +133 -0
  22. openai_sdk_helpers/logging_config.py +105 -0
  23. openai_sdk_helpers/prompt/__init__.py +10 -71
  24. openai_sdk_helpers/prompt/base.py +172 -0
  25. openai_sdk_helpers/response/__init__.py +37 -5
  26. openai_sdk_helpers/response/base.py +427 -189
  27. openai_sdk_helpers/response/config.py +176 -0
  28. openai_sdk_helpers/response/messages.py +104 -40
  29. openai_sdk_helpers/response/runner.py +79 -35
  30. openai_sdk_helpers/response/tool_call.py +75 -12
  31. openai_sdk_helpers/response/vector_store.py +29 -16
  32. openai_sdk_helpers/retry.py +175 -0
  33. openai_sdk_helpers/streamlit_app/__init__.py +30 -0
  34. openai_sdk_helpers/streamlit_app/app.py +345 -0
  35. openai_sdk_helpers/streamlit_app/config.py +502 -0
  36. openai_sdk_helpers/streamlit_app/streamlit_web_search.py +68 -0
  37. openai_sdk_helpers/structure/__init__.py +69 -3
  38. openai_sdk_helpers/structure/agent_blueprint.py +82 -19
  39. openai_sdk_helpers/structure/base.py +245 -91
  40. openai_sdk_helpers/structure/plan/__init__.py +15 -1
  41. openai_sdk_helpers/structure/plan/enum.py +41 -5
  42. openai_sdk_helpers/structure/plan/plan.py +101 -45
  43. openai_sdk_helpers/structure/plan/task.py +38 -6
  44. openai_sdk_helpers/structure/prompt.py +21 -2
  45. openai_sdk_helpers/structure/responses.py +52 -11
  46. openai_sdk_helpers/structure/summary.py +55 -7
  47. openai_sdk_helpers/structure/validation.py +34 -6
  48. openai_sdk_helpers/structure/vector_search.py +132 -18
  49. openai_sdk_helpers/structure/web_search.py +128 -12
  50. openai_sdk_helpers/types.py +57 -0
  51. openai_sdk_helpers/utils/__init__.py +32 -1
  52. openai_sdk_helpers/utils/core.py +200 -32
  53. openai_sdk_helpers/validation.py +302 -0
  54. openai_sdk_helpers/vector_storage/__init__.py +21 -1
  55. openai_sdk_helpers/vector_storage/cleanup.py +25 -13
  56. openai_sdk_helpers/vector_storage/storage.py +124 -66
  57. openai_sdk_helpers/vector_storage/types.py +20 -19
  58. openai_sdk_helpers-0.0.9.dist-info/METADATA +550 -0
  59. openai_sdk_helpers-0.0.9.dist-info/RECORD +66 -0
  60. openai_sdk_helpers-0.0.7.dist-info/METADATA +0 -193
  61. openai_sdk_helpers-0.0.7.dist-info/RECORD +0 -51
  62. {openai_sdk_helpers-0.0.7.dist-info → openai_sdk_helpers-0.0.9.dist-info}/WHEEL +0 -0
  63. {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 helpers."""
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 helpers for vector stores."""
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
- This utility iterates over every vector store owned by the account,
16
- deleting each one after removing all of its files. Any standalone files that
17
- remain after the stores are deleted are also removed.
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
- Returns
20
- -------
21
- None
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
- This utility iterates over every file owned by the account and deletes them.
77
- It does not check for vector stores, so it will delete all files regardless
78
- of their association.
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
- Returns
81
- -------
82
- None
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