openai-sdk-helpers 0.1.0__tar.gz → 0.1.2__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 (82) hide show
  1. openai_sdk_helpers-0.1.0/README.md → openai_sdk_helpers-0.1.2/PKG-INFO +174 -0
  2. openai_sdk_helpers-0.1.0/PKG-INFO → openai_sdk_helpers-0.1.2/README.md +139 -25
  3. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/pyproject.toml +22 -4
  4. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/__init__.py +44 -7
  5. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/agent/base.py +5 -1
  6. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/agent/coordination.py +4 -5
  7. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/agent/runner.py +4 -1
  8. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/agent/search/base.py +1 -0
  9. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/agent/search/vector.py +2 -0
  10. openai_sdk_helpers-0.1.2/src/openai_sdk_helpers/cli.py +265 -0
  11. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/config.py +93 -2
  12. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/context_manager.py +1 -1
  13. openai_sdk_helpers-0.1.2/src/openai_sdk_helpers/deprecation.py +167 -0
  14. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/environment.py +3 -2
  15. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/errors.py +0 -12
  16. openai_sdk_helpers-0.1.2/src/openai_sdk_helpers/files_api.py +373 -0
  17. openai_sdk_helpers-0.1.2/src/openai_sdk_helpers/logging_config.py +34 -0
  18. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/prompt/base.py +1 -1
  19. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/response/__init__.py +7 -3
  20. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/response/base.py +217 -147
  21. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/response/config.py +16 -1
  22. openai_sdk_helpers-0.1.2/src/openai_sdk_helpers/response/files.py +392 -0
  23. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/response/messages.py +1 -0
  24. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/retry.py +1 -1
  25. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/streamlit_app/app.py +97 -7
  26. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/streamlit_app/streamlit_web_search.py +15 -8
  27. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/structure/base.py +6 -6
  28. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/structure/plan/helpers.py +1 -0
  29. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/structure/plan/task.py +7 -7
  30. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/tools.py +116 -13
  31. openai_sdk_helpers-0.1.2/src/openai_sdk_helpers/utils/__init__.py +125 -0
  32. {openai_sdk_helpers-0.1.0/src/openai_sdk_helpers → openai_sdk_helpers-0.1.2/src/openai_sdk_helpers/utils}/async_utils.py +5 -6
  33. openai_sdk_helpers-0.1.2/src/openai_sdk_helpers/utils/coercion.py +138 -0
  34. openai_sdk_helpers-0.1.2/src/openai_sdk_helpers/utils/deprecation.py +167 -0
  35. openai_sdk_helpers-0.1.2/src/openai_sdk_helpers/utils/encoding.py +189 -0
  36. openai_sdk_helpers-0.1.2/src/openai_sdk_helpers/utils/json_utils.py +98 -0
  37. openai_sdk_helpers-0.1.2/src/openai_sdk_helpers/utils/output_validation.py +448 -0
  38. openai_sdk_helpers-0.1.2/src/openai_sdk_helpers/utils/path_utils.py +46 -0
  39. {openai_sdk_helpers-0.1.0/src/openai_sdk_helpers → openai_sdk_helpers-0.1.2/src/openai_sdk_helpers/utils}/validation.py +7 -3
  40. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/vector_storage/storage.py +59 -28
  41. openai_sdk_helpers-0.1.0/src/openai_sdk_helpers/logging_config.py +0 -105
  42. openai_sdk_helpers-0.1.0/src/openai_sdk_helpers/utils/__init__.py +0 -60
  43. openai_sdk_helpers-0.1.0/src/openai_sdk_helpers/utils/core.py +0 -596
  44. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/.gitignore +0 -0
  45. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/LICENSE +0 -0
  46. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/agent/__init__.py +0 -0
  47. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/agent/config.py +0 -0
  48. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/agent/prompt_utils.py +0 -0
  49. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/agent/search/__init__.py +0 -0
  50. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/agent/search/web.py +0 -0
  51. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/agent/summarizer.py +0 -0
  52. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/agent/translator.py +0 -0
  53. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/agent/utils.py +0 -0
  54. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/agent/validation.py +0 -0
  55. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/enums/__init__.py +0 -0
  56. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/enums/base.py +0 -0
  57. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/prompt/__init__.py +0 -0
  58. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/prompt/summarizer.jinja +0 -0
  59. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/prompt/translator.jinja +0 -0
  60. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/prompt/validator.jinja +0 -0
  61. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/py.typed +0 -0
  62. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/response/runner.py +0 -0
  63. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/response/tool_call.py +0 -0
  64. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/response/vector_store.py +0 -0
  65. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/streamlit_app/__init__.py +0 -0
  66. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/streamlit_app/config.py +0 -0
  67. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/structure/__init__.py +0 -0
  68. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/structure/agent_blueprint.py +0 -0
  69. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/structure/plan/__init__.py +0 -0
  70. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/structure/plan/enum.py +0 -0
  71. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/structure/plan/plan.py +0 -0
  72. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/structure/plan/types.py +0 -0
  73. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/structure/prompt.py +0 -0
  74. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/structure/responses.py +0 -0
  75. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/structure/summary.py +0 -0
  76. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/structure/validation.py +0 -0
  77. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/structure/vector_search.py +0 -0
  78. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/structure/web_search.py +0 -0
  79. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/types.py +0 -0
  80. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/vector_storage/__init__.py +0 -0
  81. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/vector_storage/cleanup.py +0 -0
  82. {openai_sdk_helpers-0.1.0 → openai_sdk_helpers-0.1.2}/src/openai_sdk_helpers/vector_storage/types.py +0 -0
@@ -1,3 +1,38 @@
1
+ Metadata-Version: 2.4
2
+ Name: openai-sdk-helpers
3
+ Version: 0.1.2
4
+ Summary: Composable helpers for OpenAI SDK agents, prompts, and storage
5
+ Author: openai-sdk-helpers maintainers
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Classifier: Typing :: Typed
18
+ Requires-Python: >=3.10
19
+ Requires-Dist: jinja2
20
+ Requires-Dist: openai-agents<1.0.0,>=0.6.4
21
+ Requires-Dist: openai<3.0.0,>=2.14.0
22
+ Requires-Dist: pydantic<3,>=2.7
23
+ Requires-Dist: python-dotenv
24
+ Requires-Dist: streamlit
25
+ Requires-Dist: typing-extensions<5,>=4.15.0
26
+ Provides-Extra: dev
27
+ Requires-Dist: black; extra == 'dev'
28
+ Requires-Dist: black[jupyter]; extra == 'dev'
29
+ Requires-Dist: pydocstyle; extra == 'dev'
30
+ Requires-Dist: pyright; extra == 'dev'
31
+ Requires-Dist: pytest; extra == 'dev'
32
+ Requires-Dist: pytest-asyncio; extra == 'dev'
33
+ Requires-Dist: pytest-cov; extra == 'dev'
34
+ Description-Content-Type: text/markdown
35
+
1
36
  <div align="center">
2
37
 
3
38
  # openai-sdk-helpers
@@ -65,6 +100,26 @@ The `agent` module provides a higher-level abstraction for building agents, whil
65
100
  - **Tool execution framework** with custom handlers and structured outputs
66
101
  - **Session persistence** for saving and restoring conversation history
67
102
 
103
+ #### Infrastructure & Utilities
104
+ - **Centralized logger factory** for consistent application logging
105
+ - **Retry patterns** with exponential backoff and jitter
106
+ - **Output validation** framework with JSON schema, semantic, and length validators
107
+ - **CLI tool** for testing agents, validating templates, and inspecting registries
108
+ - **Deprecation utilities** for managing API changes
109
+
110
+ #### Shared Components
111
+ - **Typed structures** using Pydantic for prompts, responses, and search workflows
112
+ to ensure predictable inputs and outputs
113
+ - **OpenAI configuration management** with environment variable and `.env` file support
114
+ - **Vector storage abstraction** for seamless integration with OpenAI vector stores
115
+ - **Type-safe interfaces** with full type hints and `py.typed` marker for external projects
116
+ - **ValidatorAgent**: Check inputs and outputs against safety guardrails
117
+
118
+ #### Response Module (Built on `openai` SDK)
119
+ - **Response handling utilities** for direct API control with fine-grained message management
120
+ - **Tool execution framework** with custom handlers and structured outputs
121
+ - **Session persistence** for saving and restoring conversation history
122
+
68
123
  #### Shared Components
69
124
  - **Typed structures** using Pydantic for prompts, responses, and search workflows
70
125
  to ensure predictable inputs and outputs
@@ -251,6 +306,99 @@ response.close()
251
306
 
252
307
  ## Advanced Usage
253
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
+
254
402
  ### Custom Prompt Templates
255
403
 
256
404
  Create custom Jinja2 templates for specialized agent behaviors:
@@ -477,6 +625,31 @@ See `AGENTS.md` for detailed contributing guidelines and conventions.
477
625
  This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
478
626
  for details.
479
627
 
628
+ ## CLI Tool
629
+
630
+ The package includes a command-line tool for development and testing:
631
+
632
+ ```bash
633
+ # List all registered response configurations
634
+ openai-helpers registry list
635
+
636
+ # Inspect a specific configuration
637
+ openai-helpers registry inspect my_config
638
+
639
+ # Validate Jinja2 templates
640
+ openai-helpers template validate ./templates
641
+
642
+ # Test an agent (coming soon)
643
+ openai-helpers agent test MyAgent --input "test input"
644
+ ```
645
+
646
+ ### CLI Commands
647
+
648
+ - **registry list** - Show all registered response configurations
649
+ - **registry inspect** - Display details of a configuration
650
+ - **template validate** - Check template syntax and structure
651
+ - **agent test** - Test agents locally with sample inputs
652
+
480
653
  ## Troubleshooting
481
654
 
482
655
  ### Common Issues
@@ -498,6 +671,7 @@ OPENAI_API_KEY=your-api-key-here
498
671
  Vector search workflows require custom prompt templates. Either:
499
672
  1. Create the required `.jinja` files in your `prompt_dir`
500
673
  2. Omit the `prompt_dir` parameter to use built-in defaults (for text agents only)
674
+ 3. Use the CLI to validate templates: `openai-helpers template validate ./templates`
501
675
 
502
676
  **Import errors after installation**
503
677
 
@@ -1,28 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: openai-sdk-helpers
3
- Version: 0.1.0
4
- Summary: Composable helpers for OpenAI SDK agents, prompts, and storage
5
- Author: openai-sdk-helpers maintainers
6
- License: MIT
7
- License-File: LICENSE
8
- Requires-Python: >=3.10
9
- Requires-Dist: jinja2
10
- Requires-Dist: openai
11
- Requires-Dist: openai-agents
12
- Requires-Dist: pydantic<3,>=2.7
13
- Requires-Dist: python-dotenv
14
- Requires-Dist: streamlit
15
- Requires-Dist: typing-extensions<5,>=4.15.0
16
- Provides-Extra: dev
17
- Requires-Dist: black; extra == 'dev'
18
- Requires-Dist: black[jupyter]; extra == 'dev'
19
- Requires-Dist: pydocstyle; extra == 'dev'
20
- Requires-Dist: pyright; extra == 'dev'
21
- Requires-Dist: pytest; extra == 'dev'
22
- Requires-Dist: pytest-asyncio; extra == 'dev'
23
- Requires-Dist: pytest-cov; extra == 'dev'
24
- Description-Content-Type: text/markdown
25
-
26
1
  <div align="center">
27
2
 
28
3
  # openai-sdk-helpers
@@ -90,6 +65,26 @@ The `agent` module provides a higher-level abstraction for building agents, whil
90
65
  - **Tool execution framework** with custom handlers and structured outputs
91
66
  - **Session persistence** for saving and restoring conversation history
92
67
 
68
+ #### Infrastructure & Utilities
69
+ - **Centralized logger factory** for consistent application logging
70
+ - **Retry patterns** with exponential backoff and jitter
71
+ - **Output validation** framework with JSON schema, semantic, and length validators
72
+ - **CLI tool** for testing agents, validating templates, and inspecting registries
73
+ - **Deprecation utilities** for managing API changes
74
+
75
+ #### Shared Components
76
+ - **Typed structures** using Pydantic for prompts, responses, and search workflows
77
+ to ensure predictable inputs and outputs
78
+ - **OpenAI configuration management** with environment variable and `.env` file support
79
+ - **Vector storage abstraction** for seamless integration with OpenAI vector stores
80
+ - **Type-safe interfaces** with full type hints and `py.typed` marker for external projects
81
+ - **ValidatorAgent**: Check inputs and outputs against safety guardrails
82
+
83
+ #### Response Module (Built on `openai` SDK)
84
+ - **Response handling utilities** for direct API control with fine-grained message management
85
+ - **Tool execution framework** with custom handlers and structured outputs
86
+ - **Session persistence** for saving and restoring conversation history
87
+
93
88
  #### Shared Components
94
89
  - **Typed structures** using Pydantic for prompts, responses, and search workflows
95
90
  to ensure predictable inputs and outputs
@@ -276,6 +271,99 @@ response.close()
276
271
 
277
272
  ## Advanced Usage
278
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
+
279
367
  ### Custom Prompt Templates
280
368
 
281
369
  Create custom Jinja2 templates for specialized agent behaviors:
@@ -502,6 +590,31 @@ See `AGENTS.md` for detailed contributing guidelines and conventions.
502
590
  This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
503
591
  for details.
504
592
 
593
+ ## CLI Tool
594
+
595
+ The package includes a command-line tool for development and testing:
596
+
597
+ ```bash
598
+ # List all registered response configurations
599
+ openai-helpers registry list
600
+
601
+ # Inspect a specific configuration
602
+ openai-helpers registry inspect my_config
603
+
604
+ # Validate Jinja2 templates
605
+ openai-helpers template validate ./templates
606
+
607
+ # Test an agent (coming soon)
608
+ openai-helpers agent test MyAgent --input "test input"
609
+ ```
610
+
611
+ ### CLI Commands
612
+
613
+ - **registry list** - Show all registered response configurations
614
+ - **registry inspect** - Display details of a configuration
615
+ - **template validate** - Check template syntax and structure
616
+ - **agent test** - Test agents locally with sample inputs
617
+
505
618
  ## Troubleshooting
506
619
 
507
620
  ### Common Issues
@@ -523,6 +636,7 @@ OPENAI_API_KEY=your-api-key-here
523
636
  Vector search workflows require custom prompt templates. Either:
524
637
  1. Create the required `.jinja` files in your `prompt_dir`
525
638
  2. Omit the `prompt_dir` parameter to use built-in defaults (for text agents only)
639
+ 3. Use the CLI to validate templates: `openai-helpers template validate ./templates`
526
640
 
527
641
  **Import errors after installation**
528
642
 
@@ -1,11 +1,23 @@
1
1
  [project]
2
2
  name = "openai-sdk-helpers"
3
- version = "0.1.0"
3
+ version = "0.1.2"
4
4
  requires-python = ">=3.10"
5
5
  readme = "README.md"
6
6
  description = "Composable helpers for OpenAI SDK agents, prompts, and storage"
7
7
  license = {text = "MIT"}
8
8
  authors = [{name = "openai-sdk-helpers maintainers"}]
9
+ classifiers = [
10
+ "Development Status :: 4 - Beta",
11
+ "Intended Audience :: Developers",
12
+ "License :: OSI Approved :: MIT License",
13
+ "Programming Language :: Python :: 3",
14
+ "Programming Language :: Python :: 3.10",
15
+ "Programming Language :: Python :: 3.11",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Programming Language :: Python :: 3.13",
18
+ "Topic :: Software Development :: Libraries :: Python Modules",
19
+ "Typing :: Typed",
20
+ ]
9
21
  dependencies = [
10
22
 
11
23
  # Template rendering
@@ -21,9 +33,9 @@ dependencies = [
21
33
  "python-dotenv",
22
34
 
23
35
 
24
- # OpenAI functionality
25
- "openai",
26
- "openai-agents",
36
+ # OpenAI functionality (tested with openai==2.14.0, openai-agents==0.6.4)
37
+ "openai>=2.14.0,<3.0.0",
38
+ "openai-agents>=0.6.4,<1.0.0",
27
39
 
28
40
 
29
41
  # Web UI
@@ -66,3 +78,9 @@ extraPaths = ["src"]
66
78
 
67
79
  [tool.pydocstyle]
68
80
  convention = "numpy"
81
+
82
+ [tool.pytest.ini_options]
83
+ asyncio_mode = "auto"
84
+
85
+ [project.scripts]
86
+ openai-helpers = "openai_sdk_helpers.cli:main"
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from .async_utils import run_coroutine_thread_safe, run_coroutine_with_fallback
5
+ from .utils.async_utils import run_coroutine_thread_safe, run_coroutine_with_fallback
6
6
  from .context_manager import (
7
7
  AsyncManagedResource,
8
8
  ManagedResource,
@@ -22,9 +22,8 @@ from .errors import (
22
22
  AsyncExecutionError,
23
23
  ResourceCleanupError,
24
24
  )
25
- from .logging_config import LoggerFactory
26
25
  from .retry import with_exponential_backoff
27
- from .validation import (
26
+ from .utils.validation import (
28
27
  validate_choice,
29
28
  validate_dict_mapping,
30
29
  validate_list_items,
@@ -52,6 +51,7 @@ from .structure import (
52
51
  )
53
52
  from .prompt import PromptRenderer
54
53
  from .config import OpenAISettings
54
+ from .files_api import FilesAPIManager, FilePurpose
55
55
  from .vector_storage import VectorStorage, VectorStorageFileInfo, VectorStorageFileStats
56
56
  from .agent import (
57
57
  AgentBase,
@@ -78,9 +78,28 @@ from .response import (
78
78
  from .tools import (
79
79
  serialize_tool_result,
80
80
  tool_handler_factory,
81
+ StructureType,
82
+ ToolSpec,
83
+ build_tool_definitions,
81
84
  )
82
- from .utils import (
83
- build_openai_settings,
85
+ from .config import build_openai_settings
86
+ from .utils.deprecation import (
87
+ deprecated,
88
+ warn_deprecated,
89
+ DeprecationHelper,
90
+ )
91
+ from .utils.output_validation import (
92
+ ValidationResult,
93
+ ValidationRule,
94
+ JSONSchemaValidator,
95
+ SemanticValidator,
96
+ LengthValidator,
97
+ OutputValidator,
98
+ validate_output,
99
+ )
100
+ from .types import (
101
+ SupportsOpenAIClient,
102
+ OpenAIClient,
84
103
  )
85
104
 
86
105
  __all__ = [
@@ -98,8 +117,6 @@ __all__ = [
98
117
  "InputValidationError",
99
118
  "AsyncExecutionError",
100
119
  "ResourceCleanupError",
101
- # Logging
102
- "LoggerFactory",
103
120
  # Retry utilities
104
121
  "with_exponential_backoff",
105
122
  # Context managers
@@ -122,6 +139,8 @@ __all__ = [
122
139
  "spec_field",
123
140
  "PromptRenderer",
124
141
  "OpenAISettings",
142
+ "FilesAPIManager",
143
+ "FilePurpose",
125
144
  "VectorStorage",
126
145
  "VectorStorageFileInfo",
127
146
  "VectorStorageFileStats",
@@ -154,8 +173,26 @@ __all__ = [
154
173
  "attach_vector_store",
155
174
  "serialize_tool_result",
156
175
  "tool_handler_factory",
176
+ "StructureType",
177
+ "ToolSpec",
178
+ "build_tool_definitions",
157
179
  "build_openai_settings",
158
180
  "create_plan",
159
181
  "execute_task",
160
182
  "execute_plan",
183
+ # Type definitions
184
+ "SupportsOpenAIClient",
185
+ "OpenAIClient",
186
+ # Deprecation utilities
187
+ "deprecated",
188
+ "warn_deprecated",
189
+ "DeprecationHelper",
190
+ # Output validation
191
+ "ValidationResult",
192
+ "ValidationRule",
193
+ "JSONSchemaValidator",
194
+ "SemanticValidator",
195
+ "LengthValidator",
196
+ "OutputValidator",
197
+ "validate_output",
161
198
  ]
@@ -146,6 +146,7 @@ class AgentBase:
146
146
  def from_config(
147
147
  cls,
148
148
  config: AgentConfigLike,
149
+ *,
149
150
  run_context_wrapper: Optional[RunContextWrapper[Dict[str, Any]]] = None,
150
151
  prompt_dir: Optional[Path] = None,
151
152
  default_model: Optional[str] = None,
@@ -213,7 +214,7 @@ class AgentBase:
213
214
  return self._template.render(context)
214
215
 
215
216
  def get_prompt(
216
- self, run_context_wrapper: RunContextWrapper[Dict[str, Any]], _: Agent
217
+ self, run_context_wrapper: RunContextWrapper[Dict[str, Any]], *, _: Agent
217
218
  ) -> str:
218
219
  """Render the agent prompt using the provided run context.
219
220
 
@@ -257,6 +258,7 @@ class AgentBase:
257
258
  async def run_async(
258
259
  self,
259
260
  input: str,
261
+ *,
260
262
  context: Optional[Dict[str, Any]] = None,
261
263
  output_type: Optional[Any] = None,
262
264
  ) -> Any:
@@ -288,6 +290,7 @@ class AgentBase:
288
290
  def run_sync(
289
291
  self,
290
292
  input: str,
293
+ *,
291
294
  context: Optional[Dict[str, Any]] = None,
292
295
  output_type: Optional[Any] = None,
293
296
  ) -> Any:
@@ -317,6 +320,7 @@ class AgentBase:
317
320
  def run_streamed(
318
321
  self,
319
322
  input: str,
323
+ *,
320
324
  context: Optional[Dict[str, Any]] = None,
321
325
  output_type: Optional[Any] = None,
322
326
  ) -> RunResultStreaming:
@@ -12,8 +12,7 @@ from typing import Any, Callable, Dict, List, Optional
12
12
 
13
13
 
14
14
  from ..structure import TaskStructure, PlanStructure, PromptStructure
15
- from ..environment import DATETIME_FMT
16
- from ..utils import JSONSerializable, log
15
+ from ..utils import JSONSerializable, ensure_directory, log
17
16
  from .base import AgentBase
18
17
  from .config import AgentConfig
19
18
  from ..structure.plan.enum import AgentEnum
@@ -49,6 +48,7 @@ class CoordinatorAgent(AgentBase, JSONSerializable):
49
48
 
50
49
  def __init__(
51
50
  self,
51
+ *,
52
52
  prompt_fn: PromptFn,
53
53
  build_plan_fn: BuildPlanFn,
54
54
  execute_plan_fn: ExecutePlanFn,
@@ -207,7 +207,7 @@ class CoordinatorAgent(AgentBase, JSONSerializable):
207
207
  """
208
208
  if not self.start_date:
209
209
  self.start_date = datetime.now(timezone.utc)
210
- start_date_str = self.start_date.strftime(DATETIME_FMT)
210
+ start_date_str = self.start_date.strftime("%Y%m%d_%H%M%S")
211
211
  return self._module_data_path / self._name / f"{start_date_str}.json"
212
212
 
213
213
  def save(self) -> Path:
@@ -447,8 +447,7 @@ class CoordinatorAgent(AgentBase, JSONSerializable):
447
447
  / "coordinator_agent"
448
448
  / timestamp
449
449
  )
450
- self._run_directory.mkdir(parents=True, exist_ok=True)
451
- return self._run_directory
450
+ return ensure_directory(self._run_directory)
452
451
 
453
452
  @staticmethod
454
453
  def _task_label(task: TaskStructure) -> str:
@@ -11,12 +11,13 @@ from typing import Any, Dict, Optional
11
11
 
12
12
  from agents import Agent, RunResult, RunResultStreaming, Runner
13
13
 
14
- from openai_sdk_helpers.async_utils import run_coroutine_with_fallback
14
+ from openai_sdk_helpers.utils.async_utils import run_coroutine_with_fallback
15
15
 
16
16
 
17
17
  async def run_async(
18
18
  agent: Agent,
19
19
  input: str,
20
+ *,
20
21
  context: Optional[Dict[str, Any]] = None,
21
22
  output_type: Optional[Any] = None,
22
23
  ) -> Any:
@@ -57,6 +58,7 @@ async def run_async(
57
58
  def run_sync(
58
59
  agent: Agent,
59
60
  input: str,
61
+ *,
60
62
  context: Optional[Dict[str, Any]] = None,
61
63
  output_type: Optional[Any] = None,
62
64
  ) -> Any:
@@ -103,6 +105,7 @@ def run_sync(
103
105
  def run_streamed(
104
106
  agent: Agent,
105
107
  input: str,
108
+ *,
106
109
  context: Optional[Dict[str, Any]] = None,
107
110
  output_type: Optional[Any] = None,
108
111
  ) -> RunResultStreaming:
@@ -117,6 +117,7 @@ class SearchToolAgent(AgentBase, Generic[ItemType, ResultType, PlanType]):
117
117
 
118
118
  def __init__(
119
119
  self,
120
+ *,
120
121
  prompt_dir: Optional[Path] = None,
121
122
  default_model: Optional[str] = None,
122
123
  max_concurrent_searches: int = 10,
@@ -80,6 +80,7 @@ class VectorSearchTool(
80
80
 
81
81
  def __init__(
82
82
  self,
83
+ *,
83
84
  prompt_dir: Optional[Path] = None,
84
85
  default_model: Optional[str] = None,
85
86
  store_name: Optional[str] = None,
@@ -256,6 +257,7 @@ class VectorSearch:
256
257
 
257
258
  def __init__(
258
259
  self,
260
+ *,
259
261
  prompt_dir: Optional[Path] = None,
260
262
  default_model: Optional[str] = None,
261
263
  vector_store_name: Optional[str] = None,