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.
- openai_sdk_helpers/__init__.py +44 -7
- openai_sdk_helpers/agent/base.py +5 -1
- openai_sdk_helpers/agent/coordination.py +4 -5
- openai_sdk_helpers/agent/runner.py +4 -1
- openai_sdk_helpers/agent/search/base.py +1 -0
- openai_sdk_helpers/agent/search/vector.py +2 -0
- openai_sdk_helpers/cli.py +265 -0
- openai_sdk_helpers/config.py +93 -2
- openai_sdk_helpers/context_manager.py +1 -1
- openai_sdk_helpers/deprecation.py +167 -0
- openai_sdk_helpers/environment.py +3 -2
- openai_sdk_helpers/errors.py +0 -12
- openai_sdk_helpers/files_api.py +373 -0
- openai_sdk_helpers/logging_config.py +24 -95
- openai_sdk_helpers/prompt/base.py +1 -1
- openai_sdk_helpers/response/__init__.py +7 -3
- openai_sdk_helpers/response/base.py +217 -147
- openai_sdk_helpers/response/config.py +16 -1
- openai_sdk_helpers/response/files.py +392 -0
- openai_sdk_helpers/response/messages.py +1 -0
- openai_sdk_helpers/retry.py +1 -1
- openai_sdk_helpers/streamlit_app/app.py +97 -7
- openai_sdk_helpers/streamlit_app/streamlit_web_search.py +15 -8
- openai_sdk_helpers/structure/base.py +6 -6
- openai_sdk_helpers/structure/plan/helpers.py +1 -0
- openai_sdk_helpers/structure/plan/task.py +7 -7
- openai_sdk_helpers/tools.py +116 -13
- openai_sdk_helpers/utils/__init__.py +100 -35
- openai_sdk_helpers/{async_utils.py → utils/async_utils.py} +5 -6
- openai_sdk_helpers/utils/coercion.py +138 -0
- openai_sdk_helpers/utils/deprecation.py +167 -0
- openai_sdk_helpers/utils/encoding.py +189 -0
- openai_sdk_helpers/utils/json_utils.py +98 -0
- openai_sdk_helpers/utils/output_validation.py +448 -0
- openai_sdk_helpers/utils/path_utils.py +46 -0
- openai_sdk_helpers/{validation.py → utils/validation.py} +7 -3
- openai_sdk_helpers/vector_storage/storage.py +59 -28
- {openai_sdk_helpers-0.1.0.dist-info → openai_sdk_helpers-0.1.2.dist-info}/METADATA +152 -3
- openai_sdk_helpers-0.1.2.dist-info/RECORD +79 -0
- openai_sdk_helpers-0.1.2.dist-info/entry_points.txt +2 -0
- openai_sdk_helpers/utils/core.py +0 -596
- openai_sdk_helpers-0.1.0.dist-info/RECORD +0 -69
- {openai_sdk_helpers-0.1.0.dist-info → openai_sdk_helpers-0.1.2.dist-info}/WHEEL +0 -0
- {openai_sdk_helpers-0.1.0.dist-info → openai_sdk_helpers-0.1.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""Deprecation utilities for managing deprecated features.
|
|
2
|
+
|
|
3
|
+
This module provides infrastructure for marking and managing deprecated
|
|
4
|
+
functions, classes, and features with consistent warning messages.
|
|
5
|
+
|
|
6
|
+
Functions
|
|
7
|
+
---------
|
|
8
|
+
deprecated
|
|
9
|
+
Decorator to mark functions or classes as deprecated.
|
|
10
|
+
warn_deprecated
|
|
11
|
+
Emit a deprecation warning with optional custom message.
|
|
12
|
+
|
|
13
|
+
Classes
|
|
14
|
+
-------
|
|
15
|
+
DeprecationHelper
|
|
16
|
+
Utility class for managing deprecation warnings and versions.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import functools
|
|
22
|
+
import warnings
|
|
23
|
+
from typing import Any, Callable, TypeVar
|
|
24
|
+
|
|
25
|
+
F = TypeVar("F", bound=Callable[..., Any])
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class DeprecationHelper:
|
|
29
|
+
"""Utility class for managing deprecation warnings.
|
|
30
|
+
|
|
31
|
+
Provides consistent formatting and control of deprecation warnings
|
|
32
|
+
across the package.
|
|
33
|
+
|
|
34
|
+
Methods
|
|
35
|
+
-------
|
|
36
|
+
warn
|
|
37
|
+
Emit a deprecation warning with standard formatting.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def warn(
|
|
42
|
+
feature_name: str,
|
|
43
|
+
removal_version: str,
|
|
44
|
+
alternative: str | None = None,
|
|
45
|
+
extra_message: str | None = None,
|
|
46
|
+
) -> None:
|
|
47
|
+
"""Emit a deprecation warning for a feature.
|
|
48
|
+
|
|
49
|
+
Parameters
|
|
50
|
+
----------
|
|
51
|
+
feature_name : str
|
|
52
|
+
Name of the deprecated feature (e.g., "MyClass.old_method").
|
|
53
|
+
removal_version : str
|
|
54
|
+
Version in which the feature will be removed.
|
|
55
|
+
alternative : str, optional
|
|
56
|
+
Recommended alternative to use instead.
|
|
57
|
+
extra_message : str, optional
|
|
58
|
+
Additional context or migration instructions.
|
|
59
|
+
|
|
60
|
+
Raises
|
|
61
|
+
------
|
|
62
|
+
DeprecationWarning
|
|
63
|
+
Always issues a DeprecationWarning to stderr.
|
|
64
|
+
"""
|
|
65
|
+
msg = f"{feature_name} is deprecated and will be removed in version {removal_version}."
|
|
66
|
+
if alternative:
|
|
67
|
+
msg += f" Use {alternative} instead."
|
|
68
|
+
if extra_message:
|
|
69
|
+
msg += f" {extra_message}"
|
|
70
|
+
|
|
71
|
+
warnings.warn(msg, DeprecationWarning, stacklevel=3)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def deprecated(
|
|
75
|
+
removal_version: str,
|
|
76
|
+
alternative: str | None = None,
|
|
77
|
+
extra_message: str | None = None,
|
|
78
|
+
) -> Callable[[F], F]:
|
|
79
|
+
"""Mark a function or class as deprecated.
|
|
80
|
+
|
|
81
|
+
Parameters
|
|
82
|
+
----------
|
|
83
|
+
removal_version : str
|
|
84
|
+
Version in which the decorated feature will be removed.
|
|
85
|
+
alternative : str, optional
|
|
86
|
+
Recommended alternative to use instead.
|
|
87
|
+
extra_message : str, optional
|
|
88
|
+
Additional context or migration instructions.
|
|
89
|
+
|
|
90
|
+
Returns
|
|
91
|
+
-------
|
|
92
|
+
Callable
|
|
93
|
+
Decorator function that wraps the target function or class.
|
|
94
|
+
|
|
95
|
+
Examples
|
|
96
|
+
--------
|
|
97
|
+
>>> @deprecated("1.0.0", "new_function")
|
|
98
|
+
... def old_function():
|
|
99
|
+
... pass
|
|
100
|
+
|
|
101
|
+
>>> class OldClass:
|
|
102
|
+
... @deprecated("1.0.0", "NewClass")
|
|
103
|
+
... def old_method(self):
|
|
104
|
+
... pass
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def decorator(func_or_class: F) -> F:
|
|
108
|
+
feature_name = f"{func_or_class.__module__}.{func_or_class.__qualname__}"
|
|
109
|
+
|
|
110
|
+
if isinstance(func_or_class, type):
|
|
111
|
+
# Handle class deprecation
|
|
112
|
+
original_init = func_or_class.__init__
|
|
113
|
+
|
|
114
|
+
@functools.wraps(original_init)
|
|
115
|
+
def new_init(self: Any, *args: Any, **kwargs: Any) -> None:
|
|
116
|
+
DeprecationHelper.warn(
|
|
117
|
+
feature_name,
|
|
118
|
+
removal_version,
|
|
119
|
+
alternative,
|
|
120
|
+
extra_message,
|
|
121
|
+
)
|
|
122
|
+
original_init(self, *args, **kwargs)
|
|
123
|
+
|
|
124
|
+
func_or_class.__init__ = new_init
|
|
125
|
+
else:
|
|
126
|
+
# Handle function deprecation
|
|
127
|
+
@functools.wraps(func_or_class)
|
|
128
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
129
|
+
DeprecationHelper.warn(
|
|
130
|
+
feature_name,
|
|
131
|
+
removal_version,
|
|
132
|
+
alternative,
|
|
133
|
+
extra_message,
|
|
134
|
+
)
|
|
135
|
+
return func_or_class(*args, **kwargs)
|
|
136
|
+
|
|
137
|
+
return wrapper # type: ignore
|
|
138
|
+
|
|
139
|
+
return func_or_class # type: ignore[return-value]
|
|
140
|
+
|
|
141
|
+
return decorator
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def warn_deprecated(
|
|
145
|
+
feature_name: str,
|
|
146
|
+
removal_version: str,
|
|
147
|
+
alternative: str | None = None,
|
|
148
|
+
extra_message: str | None = None,
|
|
149
|
+
) -> None:
|
|
150
|
+
"""Issue a deprecation warning.
|
|
151
|
+
|
|
152
|
+
Parameters
|
|
153
|
+
----------
|
|
154
|
+
feature_name : str
|
|
155
|
+
Name of the deprecated feature.
|
|
156
|
+
removal_version : str
|
|
157
|
+
Version in which the feature will be removed.
|
|
158
|
+
alternative : str, optional
|
|
159
|
+
Recommended alternative to use instead.
|
|
160
|
+
extra_message : str, optional
|
|
161
|
+
Additional context or migration instructions.
|
|
162
|
+
|
|
163
|
+
Examples
|
|
164
|
+
--------
|
|
165
|
+
>>> warn_deprecated("old_config_key", "1.0.0", "new_config_key")
|
|
166
|
+
"""
|
|
167
|
+
DeprecationHelper.warn(feature_name, removal_version, alternative, extra_message)
|
|
@@ -20,6 +20,8 @@ from __future__ import annotations
|
|
|
20
20
|
|
|
21
21
|
from pathlib import Path
|
|
22
22
|
|
|
23
|
+
from openai_sdk_helpers.utils import ensure_directory
|
|
24
|
+
|
|
23
25
|
DATETIME_FMT = "%Y%m%d_%H%M%S"
|
|
24
26
|
DEFAULT_MODEL = "gpt-4o-mini"
|
|
25
27
|
|
|
@@ -50,5 +52,4 @@ def get_data_path(name: str) -> Path:
|
|
|
50
52
|
"""
|
|
51
53
|
base = Path.home() / ".openai-sdk-helpers"
|
|
52
54
|
path = base / name
|
|
53
|
-
path
|
|
54
|
-
return path
|
|
55
|
+
return ensure_directory(path)
|
openai_sdk_helpers/errors.py
CHANGED
|
@@ -4,11 +4,8 @@ Provides specific exception types for different error scenarios,
|
|
|
4
4
|
improving error handling and debugging capabilities.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
import logging
|
|
8
7
|
from collections.abc import Mapping
|
|
9
8
|
|
|
10
|
-
from openai_sdk_helpers.utils.core import log
|
|
11
|
-
|
|
12
9
|
|
|
13
10
|
class OpenAISDKError(Exception):
|
|
14
11
|
"""Base exception for openai-sdk-helpers library.
|
|
@@ -40,15 +37,6 @@ class OpenAISDKError(Exception):
|
|
|
40
37
|
"""Initialize the exception with message and optional context."""
|
|
41
38
|
super().__init__(message)
|
|
42
39
|
self.context = dict(context) if context is not None else {}
|
|
43
|
-
self._log_context()
|
|
44
|
-
|
|
45
|
-
def _log_context(self) -> None:
|
|
46
|
-
"""Log error with context for debugging."""
|
|
47
|
-
context_str = f"\nContext: {self.context}" if self.context else ""
|
|
48
|
-
log(
|
|
49
|
-
f"{self.__class__.__name__}: {str(self)}{context_str}",
|
|
50
|
-
level=logging.ERROR,
|
|
51
|
-
)
|
|
52
40
|
|
|
53
41
|
|
|
54
42
|
class ConfigurationError(OpenAISDKError):
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
"""Comprehensive OpenAI Files API wrapper.
|
|
2
|
+
|
|
3
|
+
This module provides a complete, professional implementation of the OpenAI Files API
|
|
4
|
+
with automatic file tracking, lifecycle management, and cleanup capabilities.
|
|
5
|
+
|
|
6
|
+
References
|
|
7
|
+
----------
|
|
8
|
+
OpenAI Files API: https://platform.openai.com/docs/api-reference/files
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import logging
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any, BinaryIO, Literal, cast
|
|
16
|
+
|
|
17
|
+
from openai import OpenAI, NOT_GIVEN
|
|
18
|
+
from openai.types import FileDeleted, FileObject
|
|
19
|
+
from openai.pagination import SyncCursorPage
|
|
20
|
+
|
|
21
|
+
from .utils import log
|
|
22
|
+
|
|
23
|
+
# Valid purposes for file uploads
|
|
24
|
+
FilePurpose = Literal[
|
|
25
|
+
"assistants",
|
|
26
|
+
"batch",
|
|
27
|
+
"fine-tune",
|
|
28
|
+
"user_data",
|
|
29
|
+
"vision",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class FilesAPIManager:
|
|
34
|
+
"""Comprehensive manager for OpenAI Files API operations.
|
|
35
|
+
|
|
36
|
+
Provides full access to the OpenAI Files API with automatic file tracking,
|
|
37
|
+
lifecycle management, and cleanup capabilities. Tracks all uploaded files
|
|
38
|
+
and ensures proper deletion on cleanup.
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
client : OpenAI
|
|
43
|
+
OpenAI client instance for API calls.
|
|
44
|
+
auto_track : bool, default True
|
|
45
|
+
Automatically track uploaded files for cleanup.
|
|
46
|
+
|
|
47
|
+
Attributes
|
|
48
|
+
----------
|
|
49
|
+
tracked_files : dict[str, FileObject]
|
|
50
|
+
Dictionary of tracked file IDs to FileObject instances.
|
|
51
|
+
|
|
52
|
+
Methods
|
|
53
|
+
-------
|
|
54
|
+
create(file, purpose)
|
|
55
|
+
Upload a file to OpenAI Files API.
|
|
56
|
+
retrieve(file_id)
|
|
57
|
+
Retrieve information about a specific file.
|
|
58
|
+
list(purpose, limit)
|
|
59
|
+
List files, optionally filtered by purpose.
|
|
60
|
+
delete(file_id)
|
|
61
|
+
Delete a specific file.
|
|
62
|
+
retrieve_content(file_id)
|
|
63
|
+
Download file content.
|
|
64
|
+
cleanup()
|
|
65
|
+
Delete all tracked files.
|
|
66
|
+
|
|
67
|
+
Examples
|
|
68
|
+
--------
|
|
69
|
+
>>> from openai import OpenAI
|
|
70
|
+
>>> from openai_sdk_helpers.files_api import FilesAPIManager
|
|
71
|
+
>>>
|
|
72
|
+
>>> client = OpenAI()
|
|
73
|
+
>>> files_manager = FilesAPIManager(client)
|
|
74
|
+
>>>
|
|
75
|
+
>>> # Upload a file
|
|
76
|
+
>>> with open("document.pdf", "rb") as f:
|
|
77
|
+
... file_obj = files_manager.create(f, purpose="user_data")
|
|
78
|
+
>>>
|
|
79
|
+
>>> # List all user data files
|
|
80
|
+
>>> user_files = files_manager.list(purpose="user_data")
|
|
81
|
+
>>>
|
|
82
|
+
>>> # Retrieve file content
|
|
83
|
+
>>> content = files_manager.retrieve_content(file_obj.id)
|
|
84
|
+
>>>
|
|
85
|
+
>>> # Clean up all tracked files
|
|
86
|
+
>>> files_manager.cleanup()
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
def __init__(self, client: OpenAI, auto_track: bool = True):
|
|
90
|
+
"""Initialize the Files API manager.
|
|
91
|
+
|
|
92
|
+
Parameters
|
|
93
|
+
----------
|
|
94
|
+
client : OpenAI
|
|
95
|
+
OpenAI client instance.
|
|
96
|
+
auto_track : bool, default True
|
|
97
|
+
Automatically track uploaded files for cleanup.
|
|
98
|
+
"""
|
|
99
|
+
self._client = client
|
|
100
|
+
self._auto_track = auto_track
|
|
101
|
+
self.tracked_files: dict[str, FileObject] = {}
|
|
102
|
+
|
|
103
|
+
def create(
|
|
104
|
+
self,
|
|
105
|
+
file: BinaryIO | Path | str,
|
|
106
|
+
purpose: FilePurpose,
|
|
107
|
+
track: bool | None = None,
|
|
108
|
+
expires_after: int | None = None,
|
|
109
|
+
) -> FileObject:
|
|
110
|
+
"""Upload a file to the OpenAI Files API.
|
|
111
|
+
|
|
112
|
+
Parameters
|
|
113
|
+
----------
|
|
114
|
+
file : BinaryIO, Path, or str
|
|
115
|
+
File-like object, path to file, or file path string.
|
|
116
|
+
purpose : FilePurpose
|
|
117
|
+
The intended purpose of the uploaded file.
|
|
118
|
+
Options: "assistants", "batch", "fine-tune", "user_data", "vision"
|
|
119
|
+
track : bool or None, default None
|
|
120
|
+
Override auto_track for this file. If None, uses instance setting.
|
|
121
|
+
expires_after : int or None, default None
|
|
122
|
+
Number of seconds after which the file expires and is deleted.
|
|
123
|
+
If None and purpose is "user_data", defaults to 86400 (24 hours).
|
|
124
|
+
For other purposes, files don't expire unless explicitly set.
|
|
125
|
+
|
|
126
|
+
Returns
|
|
127
|
+
-------
|
|
128
|
+
FileObject
|
|
129
|
+
Information about the uploaded file.
|
|
130
|
+
|
|
131
|
+
Raises
|
|
132
|
+
------
|
|
133
|
+
FileNotFoundError
|
|
134
|
+
If file path doesn't exist.
|
|
135
|
+
ValueError
|
|
136
|
+
If purpose is invalid.
|
|
137
|
+
|
|
138
|
+
Examples
|
|
139
|
+
--------
|
|
140
|
+
>>> # Upload from file path (user_data expires in 24h by default)
|
|
141
|
+
>>> file_obj = manager.create("data.jsonl", purpose="user_data")
|
|
142
|
+
>>>
|
|
143
|
+
>>> # Upload with custom expiration (1 hour)
|
|
144
|
+
>>> file_obj = manager.create("temp.txt", purpose="user_data", expires_after=3600)
|
|
145
|
+
>>>
|
|
146
|
+
>>> # Upload from file handle
|
|
147
|
+
>>> with open("image.png", "rb") as f:
|
|
148
|
+
... file_obj = manager.create(f, purpose="vision")
|
|
149
|
+
>>>
|
|
150
|
+
>>> # Upload without tracking
|
|
151
|
+
>>> file_obj = manager.create("temp.txt", purpose="user_data", track=False)
|
|
152
|
+
"""
|
|
153
|
+
should_track = track if track is not None else self._auto_track
|
|
154
|
+
|
|
155
|
+
# Default to 24 hours expiration for user_data files
|
|
156
|
+
if expires_after is None and purpose == "user_data":
|
|
157
|
+
expires_after = 86400 # 24 hours in seconds
|
|
158
|
+
|
|
159
|
+
# Handle different file input types
|
|
160
|
+
# Prepare expires_after in OpenAI API format if provided
|
|
161
|
+
expires_after_param = None
|
|
162
|
+
if expires_after is not None:
|
|
163
|
+
expires_after_param = cast(
|
|
164
|
+
Any, {"anchor": "created_at", "seconds": expires_after}
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
if isinstance(file, (Path, str)):
|
|
168
|
+
file_path = Path(file).resolve()
|
|
169
|
+
if not file_path.exists():
|
|
170
|
+
raise FileNotFoundError(f"File not found: {file}")
|
|
171
|
+
|
|
172
|
+
# Use only the basename as filename (remove path)
|
|
173
|
+
filename = file_path.name
|
|
174
|
+
with open(file_path, "rb") as f:
|
|
175
|
+
# Pass tuple (filename, file_data) to set custom filename
|
|
176
|
+
if expires_after_param is not None:
|
|
177
|
+
file_obj = self._client.files.create(
|
|
178
|
+
file=(filename, f),
|
|
179
|
+
purpose=purpose,
|
|
180
|
+
expires_after=expires_after_param,
|
|
181
|
+
)
|
|
182
|
+
else:
|
|
183
|
+
file_obj = self._client.files.create(
|
|
184
|
+
file=(filename, f), purpose=purpose
|
|
185
|
+
)
|
|
186
|
+
else:
|
|
187
|
+
# Assume it's a BinaryIO
|
|
188
|
+
if expires_after_param is not None:
|
|
189
|
+
file_obj = self._client.files.create(
|
|
190
|
+
file=file,
|
|
191
|
+
purpose=purpose,
|
|
192
|
+
expires_after=expires_after_param,
|
|
193
|
+
)
|
|
194
|
+
else:
|
|
195
|
+
file_obj = self._client.files.create(file=file, purpose=purpose)
|
|
196
|
+
|
|
197
|
+
if should_track:
|
|
198
|
+
self.tracked_files[file_obj.id] = file_obj
|
|
199
|
+
expiry_msg = f" (expires in {expires_after}s)" if expires_after else ""
|
|
200
|
+
log(
|
|
201
|
+
f"Uploaded and tracking file {file_obj.id} ({file_obj.filename}) "
|
|
202
|
+
f"with purpose '{purpose}'{expiry_msg}"
|
|
203
|
+
)
|
|
204
|
+
else:
|
|
205
|
+
log(
|
|
206
|
+
f"Uploaded file {file_obj.id} ({file_obj.filename}) "
|
|
207
|
+
f"with purpose '{purpose}' (not tracked)"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
return file_obj
|
|
211
|
+
|
|
212
|
+
def retrieve(self, file_id: str) -> FileObject:
|
|
213
|
+
"""Retrieve information about a specific file.
|
|
214
|
+
|
|
215
|
+
Parameters
|
|
216
|
+
----------
|
|
217
|
+
file_id : str
|
|
218
|
+
The ID of the file to retrieve.
|
|
219
|
+
|
|
220
|
+
Returns
|
|
221
|
+
-------
|
|
222
|
+
FileObject
|
|
223
|
+
Information about the file.
|
|
224
|
+
|
|
225
|
+
Examples
|
|
226
|
+
--------
|
|
227
|
+
>>> file_info = manager.retrieve("file-abc123")
|
|
228
|
+
>>> print(f"Filename: {file_info.filename}")
|
|
229
|
+
>>> print(f"Size: {file_info.bytes} bytes")
|
|
230
|
+
"""
|
|
231
|
+
return self._client.files.retrieve(file_id)
|
|
232
|
+
|
|
233
|
+
def list(
|
|
234
|
+
self,
|
|
235
|
+
purpose: FilePurpose | None = None,
|
|
236
|
+
limit: int | None = None,
|
|
237
|
+
) -> SyncCursorPage[FileObject]:
|
|
238
|
+
"""List files, optionally filtered by purpose.
|
|
239
|
+
|
|
240
|
+
Parameters
|
|
241
|
+
----------
|
|
242
|
+
purpose : FilePurpose or None, default None
|
|
243
|
+
Filter files by purpose. If None, returns all files.
|
|
244
|
+
limit : int or None, default None
|
|
245
|
+
Maximum number of files to return. If None, returns all.
|
|
246
|
+
|
|
247
|
+
Returns
|
|
248
|
+
-------
|
|
249
|
+
SyncCursorPage[FileObject]
|
|
250
|
+
Page of file objects matching the criteria.
|
|
251
|
+
|
|
252
|
+
Examples
|
|
253
|
+
--------
|
|
254
|
+
>>> # List all files
|
|
255
|
+
>>> all_files = manager.list()
|
|
256
|
+
>>>
|
|
257
|
+
>>> # List user data files
|
|
258
|
+
>>> user_files = manager.list(purpose="user_data")
|
|
259
|
+
>>>
|
|
260
|
+
>>> # List up to 10 files
|
|
261
|
+
>>> recent_files = manager.list(limit=10)
|
|
262
|
+
"""
|
|
263
|
+
limit_param = NOT_GIVEN if limit is None else limit
|
|
264
|
+
if purpose is not None:
|
|
265
|
+
return self._client.files.list(
|
|
266
|
+
purpose=purpose, limit=cast(Any, limit_param)
|
|
267
|
+
)
|
|
268
|
+
return self._client.files.list(limit=cast(Any, limit_param))
|
|
269
|
+
|
|
270
|
+
def delete(self, file_id: str, untrack: bool = True) -> FileDeleted:
|
|
271
|
+
"""Delete a specific file from OpenAI Files API.
|
|
272
|
+
|
|
273
|
+
Parameters
|
|
274
|
+
----------
|
|
275
|
+
file_id : str
|
|
276
|
+
The ID of the file to delete.
|
|
277
|
+
untrack : bool, default True
|
|
278
|
+
Remove from tracked files after deletion.
|
|
279
|
+
|
|
280
|
+
Returns
|
|
281
|
+
-------
|
|
282
|
+
FileDeleted
|
|
283
|
+
Confirmation of file deletion.
|
|
284
|
+
|
|
285
|
+
Examples
|
|
286
|
+
--------
|
|
287
|
+
>>> result = manager.delete("file-abc123")
|
|
288
|
+
>>> print(f"Deleted: {result.deleted}")
|
|
289
|
+
"""
|
|
290
|
+
result = self._client.files.delete(file_id)
|
|
291
|
+
|
|
292
|
+
if untrack and file_id in self.tracked_files:
|
|
293
|
+
del self.tracked_files[file_id]
|
|
294
|
+
log(f"Deleted and untracked file {file_id}")
|
|
295
|
+
else:
|
|
296
|
+
log(f"Deleted file {file_id}")
|
|
297
|
+
|
|
298
|
+
return result
|
|
299
|
+
|
|
300
|
+
def retrieve_content(self, file_id: str) -> bytes:
|
|
301
|
+
"""Download and retrieve the content of a file.
|
|
302
|
+
|
|
303
|
+
Parameters
|
|
304
|
+
----------
|
|
305
|
+
file_id : str
|
|
306
|
+
The ID of the file to download.
|
|
307
|
+
|
|
308
|
+
Returns
|
|
309
|
+
-------
|
|
310
|
+
bytes
|
|
311
|
+
The raw bytes of the file content.
|
|
312
|
+
|
|
313
|
+
Examples
|
|
314
|
+
--------
|
|
315
|
+
>>> content = manager.retrieve_content("file-abc123")
|
|
316
|
+
>>> with open("downloaded.pdf", "wb") as f:
|
|
317
|
+
... f.write(content)
|
|
318
|
+
"""
|
|
319
|
+
return self._client.files.content(file_id).read()
|
|
320
|
+
|
|
321
|
+
def cleanup(self) -> dict[str, bool]:
|
|
322
|
+
"""Delete all tracked files.
|
|
323
|
+
|
|
324
|
+
Returns
|
|
325
|
+
-------
|
|
326
|
+
dict[str, bool]
|
|
327
|
+
Dictionary mapping file IDs to deletion success status.
|
|
328
|
+
|
|
329
|
+
Examples
|
|
330
|
+
--------
|
|
331
|
+
>>> results = manager.cleanup()
|
|
332
|
+
>>> print(f"Deleted {sum(results.values())} files")
|
|
333
|
+
"""
|
|
334
|
+
results = {}
|
|
335
|
+
file_ids = list(self.tracked_files.keys())
|
|
336
|
+
|
|
337
|
+
for file_id in file_ids:
|
|
338
|
+
try:
|
|
339
|
+
self.delete(file_id, untrack=True)
|
|
340
|
+
results[file_id] = True
|
|
341
|
+
except Exception as exc:
|
|
342
|
+
log(
|
|
343
|
+
f"Error deleting tracked file {file_id}: {exc}",
|
|
344
|
+
level=logging.WARNING,
|
|
345
|
+
)
|
|
346
|
+
results[file_id] = False
|
|
347
|
+
|
|
348
|
+
if results:
|
|
349
|
+
successful = sum(results.values())
|
|
350
|
+
log(f"Cleanup complete: {successful}/{len(results)} files deleted")
|
|
351
|
+
else:
|
|
352
|
+
log("No tracked files to clean up")
|
|
353
|
+
|
|
354
|
+
return results
|
|
355
|
+
|
|
356
|
+
def __enter__(self) -> FilesAPIManager:
|
|
357
|
+
"""Context manager entry."""
|
|
358
|
+
return self
|
|
359
|
+
|
|
360
|
+
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
361
|
+
"""Context manager exit with automatic cleanup."""
|
|
362
|
+
self.cleanup()
|
|
363
|
+
|
|
364
|
+
def __len__(self) -> int:
|
|
365
|
+
"""Return number of tracked files."""
|
|
366
|
+
return len(self.tracked_files)
|
|
367
|
+
|
|
368
|
+
def __repr__(self) -> str:
|
|
369
|
+
"""Return string representation of the manager."""
|
|
370
|
+
return f"FilesAPIManager(tracked_files={len(self.tracked_files)})"
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
__all__ = ["FilesAPIManager", "FilePurpose"]
|