openai-sdk-helpers 0.1.1__tar.gz → 0.1.4__tar.gz

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 (79) hide show
  1. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/PKG-INFO +94 -1
  2. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/README.md +93 -0
  3. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/pyproject.toml +1 -1
  4. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/__init__.py +3 -0
  5. openai_sdk_helpers-0.1.4/src/openai_sdk_helpers/files_api.py +373 -0
  6. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/response/__init__.py +7 -3
  7. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/response/base.py +166 -65
  8. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/response/config.py +16 -1
  9. openai_sdk_helpers-0.1.4/src/openai_sdk_helpers/response/files.py +392 -0
  10. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/streamlit_app/app.py +186 -12
  11. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/utils/__init__.py +18 -0
  12. openai_sdk_helpers-0.1.4/src/openai_sdk_helpers/utils/encoding.py +189 -0
  13. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/vector_storage/storage.py +50 -22
  14. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/.gitignore +0 -0
  15. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/LICENSE +0 -0
  16. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/__init__.py +0 -0
  17. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/base.py +0 -0
  18. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/config.py +0 -0
  19. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/coordination.py +0 -0
  20. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/prompt_utils.py +0 -0
  21. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/runner.py +0 -0
  22. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/search/__init__.py +0 -0
  23. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/search/base.py +0 -0
  24. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/search/vector.py +0 -0
  25. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/search/web.py +0 -0
  26. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/summarizer.py +0 -0
  27. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/translator.py +0 -0
  28. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/utils.py +0 -0
  29. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/validation.py +0 -0
  30. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/cli.py +0 -0
  31. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/config.py +0 -0
  32. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/context_manager.py +0 -0
  33. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/deprecation.py +0 -0
  34. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/enums/__init__.py +0 -0
  35. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/enums/base.py +0 -0
  36. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/environment.py +0 -0
  37. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/errors.py +0 -0
  38. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/logging_config.py +0 -0
  39. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/prompt/__init__.py +0 -0
  40. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/prompt/base.py +0 -0
  41. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/prompt/summarizer.jinja +0 -0
  42. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/prompt/translator.jinja +0 -0
  43. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/prompt/validator.jinja +0 -0
  44. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/py.typed +0 -0
  45. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/response/messages.py +0 -0
  46. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/response/runner.py +0 -0
  47. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/response/tool_call.py +0 -0
  48. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/response/vector_store.py +0 -0
  49. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/retry.py +0 -0
  50. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/streamlit_app/__init__.py +0 -0
  51. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/streamlit_app/config.py +0 -0
  52. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/streamlit_app/streamlit_web_search.py +0 -0
  53. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/__init__.py +0 -0
  54. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/agent_blueprint.py +0 -0
  55. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/base.py +0 -0
  56. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/plan/__init__.py +0 -0
  57. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/plan/enum.py +0 -0
  58. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/plan/helpers.py +0 -0
  59. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/plan/plan.py +0 -0
  60. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/plan/task.py +0 -0
  61. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/plan/types.py +0 -0
  62. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/prompt.py +0 -0
  63. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/responses.py +0 -0
  64. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/summary.py +0 -0
  65. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/validation.py +0 -0
  66. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/vector_search.py +0 -0
  67. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/web_search.py +0 -0
  68. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/tools.py +0 -0
  69. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/types.py +0 -0
  70. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/utils/async_utils.py +0 -0
  71. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/utils/coercion.py +0 -0
  72. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/utils/deprecation.py +0 -0
  73. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/utils/json_utils.py +0 -0
  74. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/utils/output_validation.py +0 -0
  75. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/utils/path_utils.py +0 -0
  76. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/utils/validation.py +0 -0
  77. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/vector_storage/__init__.py +0 -0
  78. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/vector_storage/cleanup.py +0 -0
  79. {openai_sdk_helpers-0.1.1 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/vector_storage/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openai-sdk-helpers
3
- Version: 0.1.1
3
+ Version: 0.1.4
4
4
  Summary: Composable helpers for OpenAI SDK agents, prompts, and storage
5
5
  Author: openai-sdk-helpers maintainers
6
6
  License: MIT
@@ -306,6 +306,99 @@ response.close()
306
306
 
307
307
  ## Advanced Usage
308
308
 
309
+ ### Image and File Analysis
310
+
311
+ The `response` module automatically detects file types and handles them appropriately:
312
+
313
+ ```python
314
+ from openai_sdk_helpers.response import BaseResponse
315
+ from openai_sdk_helpers import OpenAISettings
316
+
317
+ settings = OpenAISettings.from_env()
318
+
319
+ with BaseResponse(
320
+ name="analyzer",
321
+ instructions="You are a helpful assistant that can analyze files.",
322
+ tools=None,
323
+ output_structure=None,
324
+ tool_handlers={},
325
+ openai_settings=settings,
326
+ ) as response:
327
+ # Automatic type detection - single files parameter
328
+ # Images are sent as base64-encoded images
329
+ # Documents are sent as base64-encoded file data
330
+ result = response.run_sync(
331
+ "Analyze these files",
332
+ files=["photo.jpg", "document.pdf"]
333
+ )
334
+ print(result)
335
+
336
+ # Single file - automatically detected
337
+ result = response.run_sync(
338
+ "What's in this image?",
339
+ files="photo.jpg" # Automatically detected as image
340
+ )
341
+ print(result)
342
+
343
+ # Use vector store for RAG (Retrieval-Augmented Generation)
344
+ result = response.run_sync(
345
+ "Search these documents",
346
+ files=["doc1.pdf", "doc2.pdf"],
347
+ use_vector_store=True # Enable RAG with vector stores
348
+ )
349
+ print(result)
350
+ ```
351
+
352
+ **How It Works:**
353
+
354
+ - **Images** (jpg, png, gif, etc.) are automatically sent as base64-encoded images
355
+ - **Documents** (pdf, txt, xlsx, etc.) are sent as base64-encoded file data by default
356
+ - **Vector Stores** can optionally be used for documents when `use_vector_store=True`
357
+ - **Batch Processing** is automatically used for multiple files (>3) for efficient encoding
358
+
359
+ **Advanced File Processing:**
360
+
361
+ ```python
362
+ from openai_sdk_helpers.response import process_files
363
+
364
+ # Process files directly with the dedicated module
365
+ vector_files, base64_files, images = process_files(
366
+ response,
367
+ files=["photo1.jpg", "photo2.jpg", "doc1.pdf", "doc2.pdf"],
368
+ use_vector_store=False,
369
+ batch_size=20, # Files per batch
370
+ max_workers=10, # Concurrent workers
371
+ )
372
+ ```
373
+
374
+ **Base64 Encoding Utilities:**
375
+
376
+ ```python
377
+ from openai_sdk_helpers.utils import (
378
+ encode_image,
379
+ encode_file,
380
+ is_image_file,
381
+ create_image_data_url,
382
+ create_file_data_url,
383
+ )
384
+
385
+ # Check if a file is an image
386
+ is_image_file("photo.jpg") # True
387
+ is_image_file("document.pdf") # False
388
+
389
+ # Encode an image to base64
390
+ base64_image = encode_image("photo.jpg")
391
+
392
+ # Create a data URL for an image
393
+ image_url, detail = create_image_data_url("photo.jpg", detail="high")
394
+
395
+ # Encode a file to base64
396
+ base64_file = encode_file("document.pdf")
397
+
398
+ # Create a data URL for a file
399
+ file_data = create_file_data_url("document.pdf")
400
+ ```
401
+
309
402
  ### Custom Prompt Templates
310
403
 
311
404
  Create custom Jinja2 templates for specialized agent behaviors:
@@ -271,6 +271,99 @@ response.close()
271
271
 
272
272
  ## Advanced Usage
273
273
 
274
+ ### Image and File Analysis
275
+
276
+ The `response` module automatically detects file types and handles them appropriately:
277
+
278
+ ```python
279
+ from openai_sdk_helpers.response import BaseResponse
280
+ from openai_sdk_helpers import OpenAISettings
281
+
282
+ settings = OpenAISettings.from_env()
283
+
284
+ with BaseResponse(
285
+ name="analyzer",
286
+ instructions="You are a helpful assistant that can analyze files.",
287
+ tools=None,
288
+ output_structure=None,
289
+ tool_handlers={},
290
+ openai_settings=settings,
291
+ ) as response:
292
+ # Automatic type detection - single files parameter
293
+ # Images are sent as base64-encoded images
294
+ # Documents are sent as base64-encoded file data
295
+ result = response.run_sync(
296
+ "Analyze these files",
297
+ files=["photo.jpg", "document.pdf"]
298
+ )
299
+ print(result)
300
+
301
+ # Single file - automatically detected
302
+ result = response.run_sync(
303
+ "What's in this image?",
304
+ files="photo.jpg" # Automatically detected as image
305
+ )
306
+ print(result)
307
+
308
+ # Use vector store for RAG (Retrieval-Augmented Generation)
309
+ result = response.run_sync(
310
+ "Search these documents",
311
+ files=["doc1.pdf", "doc2.pdf"],
312
+ use_vector_store=True # Enable RAG with vector stores
313
+ )
314
+ print(result)
315
+ ```
316
+
317
+ **How It Works:**
318
+
319
+ - **Images** (jpg, png, gif, etc.) are automatically sent as base64-encoded images
320
+ - **Documents** (pdf, txt, xlsx, etc.) are sent as base64-encoded file data by default
321
+ - **Vector Stores** can optionally be used for documents when `use_vector_store=True`
322
+ - **Batch Processing** is automatically used for multiple files (>3) for efficient encoding
323
+
324
+ **Advanced File Processing:**
325
+
326
+ ```python
327
+ from openai_sdk_helpers.response import process_files
328
+
329
+ # Process files directly with the dedicated module
330
+ vector_files, base64_files, images = process_files(
331
+ response,
332
+ files=["photo1.jpg", "photo2.jpg", "doc1.pdf", "doc2.pdf"],
333
+ use_vector_store=False,
334
+ batch_size=20, # Files per batch
335
+ max_workers=10, # Concurrent workers
336
+ )
337
+ ```
338
+
339
+ **Base64 Encoding Utilities:**
340
+
341
+ ```python
342
+ from openai_sdk_helpers.utils import (
343
+ encode_image,
344
+ encode_file,
345
+ is_image_file,
346
+ create_image_data_url,
347
+ create_file_data_url,
348
+ )
349
+
350
+ # Check if a file is an image
351
+ is_image_file("photo.jpg") # True
352
+ is_image_file("document.pdf") # False
353
+
354
+ # Encode an image to base64
355
+ base64_image = encode_image("photo.jpg")
356
+
357
+ # Create a data URL for an image
358
+ image_url, detail = create_image_data_url("photo.jpg", detail="high")
359
+
360
+ # Encode a file to base64
361
+ base64_file = encode_file("document.pdf")
362
+
363
+ # Create a data URL for a file
364
+ file_data = create_file_data_url("document.pdf")
365
+ ```
366
+
274
367
  ### Custom Prompt Templates
275
368
 
276
369
  Create custom Jinja2 templates for specialized agent behaviors:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "openai-sdk-helpers"
3
- version = "0.1.1"
3
+ version = "0.1.4"
4
4
  requires-python = ">=3.10"
5
5
  readme = "README.md"
6
6
  description = "Composable helpers for OpenAI SDK agents, prompts, and storage"
@@ -51,6 +51,7 @@ from .structure import (
51
51
  )
52
52
  from .prompt import PromptRenderer
53
53
  from .config import OpenAISettings
54
+ from .files_api import FilesAPIManager, FilePurpose
54
55
  from .vector_storage import VectorStorage, VectorStorageFileInfo, VectorStorageFileStats
55
56
  from .agent import (
56
57
  AgentBase,
@@ -138,6 +139,8 @@ __all__ = [
138
139
  "spec_field",
139
140
  "PromptRenderer",
140
141
  "OpenAISettings",
142
+ "FilesAPIManager",
143
+ "FilePurpose",
141
144
  "VectorStorage",
142
145
  "VectorStorageFileInfo",
143
146
  "VectorStorageFileStats",
@@ -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"]
@@ -1,9 +1,9 @@
1
1
  """Response handling for OpenAI API interactions.
2
2
 
3
3
  This module provides comprehensive support for managing OpenAI API responses,
4
- including message handling, tool execution, vector store attachments, and
5
- structured output parsing. It serves as the foundation for building
6
- sophisticated AI agents with persistent conversation state.
4
+ including message handling, tool execution, vector store attachments, file
5
+ processing, and structured output parsing. It serves as the foundation for
6
+ building sophisticated AI agents with persistent conversation state.
7
7
 
8
8
  Classes
9
9
  -------
@@ -28,12 +28,15 @@ run_streamed
28
28
  Execute a response workflow and return the asynchronous result.
29
29
  attach_vector_store
30
30
  Attach vector stores to a response's file_search tool.
31
+ process_files
32
+ Process file attachments with automatic type detection.
31
33
  """
32
34
 
33
35
  from __future__ import annotations
34
36
 
35
37
  from .base import BaseResponse
36
38
  from .config import ResponseConfiguration, ResponseRegistry, get_default_registry
39
+ from .files import process_files
37
40
  from .messages import ResponseMessage, ResponseMessages
38
41
  from .runner import run_async, run_streamed, run_sync
39
42
  from .tool_call import ResponseToolCall, parse_tool_arguments
@@ -52,4 +55,5 @@ __all__ = [
52
55
  "ResponseToolCall",
53
56
  "parse_tool_arguments",
54
57
  "attach_vector_store",
58
+ "process_files",
55
59
  ]