openai-sdk-helpers 0.0.8__py3-none-any.whl → 0.1.0__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 (67) hide show
  1. openai_sdk_helpers/__init__.py +90 -2
  2. openai_sdk_helpers/agent/__init__.py +8 -4
  3. openai_sdk_helpers/agent/base.py +80 -45
  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} +77 -156
  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 +101 -65
  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 +222 -0
  25. openai_sdk_helpers/response/__init__.py +38 -3
  26. openai_sdk_helpers/response/base.py +363 -210
  27. openai_sdk_helpers/response/config.py +318 -0
  28. openai_sdk_helpers/response/messages.py +56 -40
  29. openai_sdk_helpers/response/runner.py +77 -33
  30. openai_sdk_helpers/response/tool_call.py +62 -27
  31. openai_sdk_helpers/response/vector_store.py +27 -14
  32. openai_sdk_helpers/retry.py +175 -0
  33. openai_sdk_helpers/streamlit_app/__init__.py +19 -2
  34. openai_sdk_helpers/streamlit_app/app.py +114 -39
  35. openai_sdk_helpers/streamlit_app/config.py +502 -0
  36. openai_sdk_helpers/streamlit_app/streamlit_web_search.py +5 -6
  37. openai_sdk_helpers/structure/__init__.py +72 -3
  38. openai_sdk_helpers/structure/agent_blueprint.py +82 -19
  39. openai_sdk_helpers/structure/base.py +208 -93
  40. openai_sdk_helpers/structure/plan/__init__.py +29 -1
  41. openai_sdk_helpers/structure/plan/enum.py +41 -5
  42. openai_sdk_helpers/structure/plan/helpers.py +172 -0
  43. openai_sdk_helpers/structure/plan/plan.py +109 -49
  44. openai_sdk_helpers/structure/plan/task.py +38 -6
  45. openai_sdk_helpers/structure/plan/types.py +15 -0
  46. openai_sdk_helpers/structure/prompt.py +21 -2
  47. openai_sdk_helpers/structure/responses.py +52 -11
  48. openai_sdk_helpers/structure/summary.py +55 -7
  49. openai_sdk_helpers/structure/validation.py +34 -6
  50. openai_sdk_helpers/structure/vector_search.py +132 -18
  51. openai_sdk_helpers/structure/web_search.py +125 -13
  52. openai_sdk_helpers/tools.py +193 -0
  53. openai_sdk_helpers/types.py +57 -0
  54. openai_sdk_helpers/utils/__init__.py +34 -1
  55. openai_sdk_helpers/utils/core.py +296 -34
  56. openai_sdk_helpers/validation.py +302 -0
  57. openai_sdk_helpers/vector_storage/__init__.py +21 -1
  58. openai_sdk_helpers/vector_storage/cleanup.py +25 -13
  59. openai_sdk_helpers/vector_storage/storage.py +123 -64
  60. openai_sdk_helpers/vector_storage/types.py +20 -19
  61. openai_sdk_helpers-0.1.0.dist-info/METADATA +550 -0
  62. openai_sdk_helpers-0.1.0.dist-info/RECORD +69 -0
  63. openai_sdk_helpers/streamlit_app/configuration.py +0 -324
  64. openai_sdk_helpers-0.0.8.dist-info/METADATA +0 -194
  65. openai_sdk_helpers-0.0.8.dist-info/RECORD +0 -55
  66. {openai_sdk_helpers-0.0.8.dist-info → openai_sdk_helpers-0.1.0.dist-info}/WHEEL +0 -0
  67. {openai_sdk_helpers-0.0.8.dist-info → openai_sdk_helpers-0.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,29 +1,88 @@
1
- """Base enum helpers."""
1
+ """Base enum classes with metadata support.
2
+
3
+ This module defines specialized enum base classes that extend the standard
4
+ library's Enum class with additional metadata and serialization capabilities.
5
+ """
2
6
 
3
7
  from __future__ import annotations
4
8
 
5
9
  from enum import Enum
10
+ from typing import Any
6
11
 
7
12
 
8
13
  class CrosswalkJSONEnum(str, Enum):
9
- """Enum base class with crosswalk metadata hooks.
14
+ """String-based enum with crosswalk metadata support.
15
+
16
+ Extends both str and Enum to provide string-compatible enum values
17
+ with optional metadata mappings. Subclasses must implement the
18
+ CROSSWALK class method to provide structured metadata for each
19
+ enum member.
20
+
21
+ This design allows enums to carry additional context beyond their
22
+ values, useful for configuration, validation, or documentation purposes.
10
23
 
11
24
  Methods
12
25
  -------
13
26
  CROSSWALK()
14
- Return metadata describing enum values keyed by name.
27
+ Return metadata describing enum values keyed by member name.
28
+
29
+ Examples
30
+ --------
31
+ >>> class Status(CrosswalkJSONEnum):
32
+ ... ACTIVE = "active"
33
+ ... INACTIVE = "inactive"
34
+ ...
35
+ ... @classmethod
36
+ ... def CROSSWALK(cls):
37
+ ... return {
38
+ ... "ACTIVE": {"description": "Currently active"},
39
+ ... "INACTIVE": {"description": "Not active"}
40
+ ... }
41
+ >>> Status.ACTIVE.value
42
+ 'active'
43
+ >>> Status.CROSSWALK()["ACTIVE"]["description"]
44
+ 'Currently active'
15
45
  """
16
46
 
17
47
  @classmethod
18
- def CROSSWALK(cls) -> dict[str, dict[str, object]]:
19
- """Return metadata describing enum values keyed by name.
48
+ def CROSSWALK(cls) -> dict[str, dict[str, Any]]:
49
+ """Return metadata describing enum values keyed by member name.
50
+
51
+ Subclasses must override this method to provide structured
52
+ metadata for each enum member. The outer dictionary keys
53
+ correspond to enum member names, and values are dictionaries
54
+ containing arbitrary metadata.
20
55
 
21
56
  Returns
22
57
  -------
23
- dict[str, dict[str, object]]
24
- Mapping of enum member names to structured metadata details.
58
+ dict[str, dict[str, Any]]
59
+ Mapping of enum member names to their metadata dictionaries.
60
+ Each metadata dictionary can contain any relevant key-value
61
+ pairs describing that enum member.
62
+
63
+ Raises
64
+ ------
65
+ NotImplementedError
66
+ If called on a subclass that has not implemented this method.
67
+
68
+ Examples
69
+ --------
70
+ >>> class Priority(CrosswalkJSONEnum):
71
+ ... HIGH = "high"
72
+ ... LOW = "low"
73
+ ...
74
+ ... @classmethod
75
+ ... def CROSSWALK(cls):
76
+ ... return {
77
+ ... "HIGH": {"value": "high", "weight": 10},
78
+ ... "LOW": {"value": "low", "weight": 1}
79
+ ... }
80
+ >>> Priority.CROSSWALK()["HIGH"]["weight"]
81
+ 10
25
82
  """
26
- raise NotImplementedError("CROSSWALK must be implemented by subclasses.")
83
+ raise NotImplementedError(
84
+ f"{cls.__name__}.CROSSWALK() must be implemented by subclasses"
85
+ )
27
86
 
28
87
 
29
88
  __all__ = ["CrosswalkJSONEnum"]
@@ -1,27 +1,54 @@
1
- """Environment helpers for openai-sdk-helpers."""
1
+ """Environment helpers for openai-sdk-helpers.
2
+
3
+ This module provides utility functions and constants for managing paths
4
+ and environment-specific configuration.
5
+
6
+ Constants
7
+ ---------
8
+ DATETIME_FMT : str
9
+ Standard datetime format string for file naming ("%Y%m%d_%H%M%S").
10
+ DEFAULT_MODEL : str
11
+ Default OpenAI model identifier ("gpt-4o-mini").
12
+
13
+ Functions
14
+ ---------
15
+ get_data_path(name)
16
+ Return a writable data directory for the given module name.
17
+ """
2
18
 
3
19
  from __future__ import annotations
4
20
 
5
21
  from pathlib import Path
6
22
 
7
23
  DATETIME_FMT = "%Y%m%d_%H%M%S"
24
+ DEFAULT_MODEL = "gpt-4o-mini"
8
25
 
9
26
 
10
- def get_data_path(module_name: str) -> Path:
27
+ def get_data_path(name: str) -> Path:
11
28
  """Return a writable data directory for the given module name.
12
29
 
30
+ Creates a module-specific directory under ~/.openai-sdk-helpers/ for
31
+ storing data, logs, or other persistent files.
32
+
13
33
  Parameters
14
34
  ----------
15
- module_name : str
35
+ name : str
16
36
  Name of the module requesting a data directory.
17
37
 
18
38
  Returns
19
39
  -------
20
40
  Path
21
- Directory path under ``~/.openai-sdk-helpers`` specific to ``module_name``. The
22
- directory is created if it does not already exist.
41
+ Directory path under ~/.openai-sdk-helpers specific to name.
42
+ The directory is created if it does not exist.
43
+
44
+ Examples
45
+ --------
46
+ >>> from openai_sdk_helpers.environment import get_data_path
47
+ >>> path = get_data_path("my_module")
48
+ >>> path.exists()
49
+ True
23
50
  """
24
51
  base = Path.home() / ".openai-sdk-helpers"
25
- path = base / module_name
52
+ path = base / name
26
53
  path.mkdir(parents=True, exist_ok=True)
27
54
  return path
@@ -0,0 +1,133 @@
1
+ """Custom exception hierarchy for openai-sdk-helpers.
2
+
3
+ Provides specific exception types for different error scenarios,
4
+ improving error handling and debugging capabilities.
5
+ """
6
+
7
+ import logging
8
+ from collections.abc import Mapping
9
+
10
+ from openai_sdk_helpers.utils.core import log
11
+
12
+
13
+ class OpenAISDKError(Exception):
14
+ """Base exception for openai-sdk-helpers library.
15
+
16
+ All custom exceptions in this library inherit from this class,
17
+ allowing callers to catch all SDK-specific errors.
18
+
19
+ Parameters
20
+ ----------
21
+ message : str
22
+ Human-readable error message
23
+ context : Mapping[str, object] | None
24
+ Additional context information for debugging. Default is None.
25
+
26
+ Examples
27
+ --------
28
+ >>> try:
29
+ ... raise OpenAISDKError("Something went wrong", context={"step": "init"})
30
+ ... except OpenAISDKError as exc:
31
+ ... print(f"SDK Error: {exc}")
32
+ ... print(f"Context: {exc.context}")
33
+ """
34
+
35
+ def __init__(
36
+ self,
37
+ message: str,
38
+ context: Mapping[str, object] | None = None,
39
+ ) -> None:
40
+ """Initialize the exception with message and optional context."""
41
+ super().__init__(message)
42
+ 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
+
53
+
54
+ class ConfigurationError(OpenAISDKError):
55
+ """Configuration validation or initialization failed.
56
+
57
+ Raised when configuration is missing, invalid, or inconsistent.
58
+ """
59
+
60
+ pass
61
+
62
+
63
+ class PromptNotFoundError(OpenAISDKError):
64
+ """Prompt template file not found or cannot be read.
65
+
66
+ Raised when a required prompt template file is missing or inaccessible.
67
+ """
68
+
69
+ pass
70
+
71
+
72
+ class AgentExecutionError(OpenAISDKError):
73
+ """Agent execution failed.
74
+
75
+ Raised when an agent encounters an error during execution.
76
+ May wrap underlying exceptions with additional context.
77
+ """
78
+
79
+ pass
80
+
81
+
82
+ class VectorStorageError(OpenAISDKError):
83
+ """Vector storage operation failed.
84
+
85
+ Raised when vector store operations (upload, download, cleanup) fail.
86
+ """
87
+
88
+ pass
89
+
90
+
91
+ class ToolExecutionError(OpenAISDKError):
92
+ """Tool execution failed.
93
+
94
+ Raised when a tool handler encounters an error.
95
+ """
96
+
97
+ pass
98
+
99
+
100
+ class ResponseGenerationError(OpenAISDKError):
101
+ """Response generation failed.
102
+
103
+ Raised when generating responses from structured output fails.
104
+ """
105
+
106
+ pass
107
+
108
+
109
+ class InputValidationError(OpenAISDKError):
110
+ """Input validation failed.
111
+
112
+ Raised when provided input doesn't meet required constraints.
113
+ """
114
+
115
+ pass
116
+
117
+
118
+ class AsyncExecutionError(OpenAISDKError):
119
+ """Asynchronous operation failed.
120
+
121
+ Raised when async/await operations fail or timeout.
122
+ """
123
+
124
+ pass
125
+
126
+
127
+ class ResourceCleanupError(OpenAISDKError):
128
+ """Resource cleanup failed.
129
+
130
+ Raised when cleanup of resources fails, but may not be fatal.
131
+ """
132
+
133
+ pass
@@ -0,0 +1,105 @@
1
+ """Centralized logging configuration for openai-sdk-helpers.
2
+
3
+ Provides a centralized factory for creating and configuring loggers
4
+ with consistent formatting and handler management.
5
+ """
6
+
7
+ import logging
8
+ import threading
9
+ from typing import Any
10
+
11
+
12
+ class LoggerFactory:
13
+ """Centralized logger creation and configuration.
14
+
15
+ Manages logger initialization and configuration to ensure consistent
16
+ logging behavior across the entire SDK. Thread-safe.
17
+
18
+ Examples
19
+ --------
20
+ Configure logging once at application startup:
21
+
22
+ >>> from openai_sdk_helpers.logging_config import LoggerFactory
23
+ >>> import logging
24
+ >>> LoggerFactory.configure(
25
+ ... level=logging.DEBUG,
26
+ ... handlers=[logging.StreamHandler()],
27
+ ... )
28
+
29
+ Get a logger instance in your module:
30
+
31
+ >>> logger = LoggerFactory.get_logger("openai_sdk_helpers.agent")
32
+ >>> logger.debug("Debug message")
33
+ """
34
+
35
+ _initialized = False
36
+ _log_level = logging.INFO
37
+ _handlers: list[logging.Handler] = []
38
+ _lock = threading.Lock()
39
+
40
+ @classmethod
41
+ def configure(
42
+ cls,
43
+ level: int = logging.INFO,
44
+ handlers: list[logging.Handler] | None = None,
45
+ ) -> None:
46
+ """Configure logging globally.
47
+
48
+ Parameters
49
+ ----------
50
+ level : int
51
+ Logging level (e.g., logging.DEBUG, logging.INFO).
52
+ Default is logging.INFO.
53
+ handlers : list[logging.Handler] | None
54
+ List of logging handlers. If None, a default
55
+ StreamHandler is created. Default is None.
56
+
57
+ Notes
58
+ -----
59
+ This method is thread-safe and can be called multiple times.
60
+ """
61
+ with cls._lock:
62
+ cls._log_level = level
63
+ if handlers:
64
+ cls._handlers = handlers
65
+ else:
66
+ handler = logging.StreamHandler()
67
+ handler.setLevel(level)
68
+ formatter = logging.Formatter(
69
+ "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
70
+ )
71
+ handler.setFormatter(formatter)
72
+ cls._handlers = [handler]
73
+ cls._initialized = True
74
+
75
+ @classmethod
76
+ def get_logger(cls, name: str) -> logging.Logger:
77
+ """Get configured logger instance.
78
+
79
+ Parameters
80
+ ----------
81
+ name : str
82
+ Logger name, typically __name__ of calling module.
83
+
84
+ Returns
85
+ -------
86
+ logging.Logger
87
+ Configured logger instance.
88
+ """
89
+ logger = logging.getLogger(name)
90
+
91
+ # Skip configuration if already configured
92
+ if logger.handlers:
93
+ return logger
94
+
95
+ with cls._lock:
96
+ if not cls._initialized:
97
+ cls.configure()
98
+
99
+ for handler in cls._handlers:
100
+ logger.addHandler(handler)
101
+
102
+ logger.setLevel(cls._log_level)
103
+ logger.propagate = False
104
+
105
+ return logger
@@ -1,77 +1,16 @@
1
- """Core prompt rendering utilities."""
1
+ """Prompt rendering utilities for template-based text generation.
2
2
 
3
- from __future__ import annotations
4
-
5
- import warnings
6
- from pathlib import Path
7
- from typing import Any, Mapping, Optional
8
-
9
- from dotenv import load_dotenv
10
- from jinja2 import Environment, FileSystemLoader, Template
11
-
12
- load_dotenv()
13
- warnings.filterwarnings("ignore")
14
-
15
-
16
- class PromptRenderer:
17
- """Render prompts using Jinja2 templates.
18
-
19
- The renderer loads templates from a base directory (defaulting to the
20
- ``prompt`` package directory) and exposes a rendering helper for
21
- injecting context values.
3
+ This module provides Jinja2-based template rendering functionality for
4
+ creating dynamic prompts with variable substitution and template inheritance.
22
5
 
23
- Methods
24
- -------
25
- render(template_path, context)
26
- Render the template at ``template_path`` with the supplied context.
27
- """
6
+ Classes
7
+ -------
8
+ PromptRenderer
9
+ Jinja2-based template renderer for dynamic prompt generation.
10
+ """
28
11
 
29
- def __init__(self, base_dir: Optional[Path] = None) -> None:
30
- """Initialize the renderer with a Jinja2 environment.
31
-
32
- Parameters
33
- ----------
34
- base_dir : Path or None, default=None
35
- Base directory containing Jinja2 templates. Defaults to the
36
- ``prompt`` directory adjacent to this file when ``None``.
37
-
38
- Returns
39
- -------
40
- None
41
- """
42
- if base_dir is None:
43
- # Defaults to the directory containing this file, which also
44
- # contains the builtin prompt templates.
45
- self.base_dir = Path(__file__).resolve().parent
46
- else:
47
- self.base_dir = base_dir
48
-
49
- self._env = Environment(
50
- loader=FileSystemLoader(str(self.base_dir)),
51
- autoescape=False, # Prompts are plain text
52
- )
53
-
54
- def render(
55
- self, template_path: str, context: Optional[Mapping[str, Any]] = None
56
- ) -> str:
57
- """Render a Jinja2 template with the given context.
58
-
59
- Parameters
60
- ----------
61
- template_path : str
62
- Path to the template file, relative to ``base_dir``.
63
- context : Mapping[str, Any] or None, default=None
64
- Context variables passed to the template.
65
-
66
- Returns
67
- -------
68
- str
69
- Rendered prompt as a string.
70
- """
71
- template_path_ = Path(self.base_dir, template_path)
72
- template_path_text = template_path_.read_text()
73
- template = Template(template_path_text)
74
- return template.render(context or {})
12
+ from __future__ import annotations
75
13
 
14
+ from .base import PromptRenderer
76
15
 
77
16
  __all__ = ["PromptRenderer"]