openai-sdk-helpers 0.0.8__py3-none-any.whl → 0.0.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openai_sdk_helpers/__init__.py +66 -2
- openai_sdk_helpers/agent/__init__.py +8 -4
- openai_sdk_helpers/agent/base.py +80 -45
- openai_sdk_helpers/agent/config.py +6 -4
- openai_sdk_helpers/agent/{project_manager.py → coordination.py} +29 -45
- openai_sdk_helpers/agent/prompt_utils.py +7 -1
- openai_sdk_helpers/agent/runner.py +67 -141
- openai_sdk_helpers/agent/search/__init__.py +33 -0
- openai_sdk_helpers/agent/search/base.py +297 -0
- openai_sdk_helpers/agent/{vector_search.py → search/vector.py} +89 -157
- openai_sdk_helpers/agent/{web_search.py → search/web.py} +77 -156
- openai_sdk_helpers/agent/summarizer.py +29 -8
- openai_sdk_helpers/agent/translator.py +40 -13
- openai_sdk_helpers/agent/validation.py +32 -8
- openai_sdk_helpers/async_utils.py +132 -0
- openai_sdk_helpers/config.py +74 -36
- openai_sdk_helpers/context_manager.py +241 -0
- openai_sdk_helpers/enums/__init__.py +9 -1
- openai_sdk_helpers/enums/base.py +67 -8
- openai_sdk_helpers/environment.py +33 -6
- openai_sdk_helpers/errors.py +133 -0
- openai_sdk_helpers/logging_config.py +105 -0
- openai_sdk_helpers/prompt/__init__.py +10 -71
- openai_sdk_helpers/prompt/base.py +172 -0
- openai_sdk_helpers/response/__init__.py +35 -3
- openai_sdk_helpers/response/base.py +363 -210
- openai_sdk_helpers/response/config.py +176 -0
- openai_sdk_helpers/response/messages.py +56 -40
- openai_sdk_helpers/response/runner.py +77 -33
- openai_sdk_helpers/response/tool_call.py +49 -25
- openai_sdk_helpers/response/vector_store.py +27 -14
- openai_sdk_helpers/retry.py +175 -0
- openai_sdk_helpers/streamlit_app/__init__.py +19 -2
- openai_sdk_helpers/streamlit_app/app.py +114 -39
- openai_sdk_helpers/streamlit_app/config.py +502 -0
- openai_sdk_helpers/streamlit_app/streamlit_web_search.py +5 -6
- openai_sdk_helpers/structure/__init__.py +69 -3
- openai_sdk_helpers/structure/agent_blueprint.py +82 -19
- openai_sdk_helpers/structure/base.py +208 -93
- openai_sdk_helpers/structure/plan/__init__.py +15 -1
- openai_sdk_helpers/structure/plan/enum.py +41 -5
- openai_sdk_helpers/structure/plan/plan.py +101 -45
- openai_sdk_helpers/structure/plan/task.py +38 -6
- openai_sdk_helpers/structure/prompt.py +21 -2
- openai_sdk_helpers/structure/responses.py +52 -11
- openai_sdk_helpers/structure/summary.py +55 -7
- openai_sdk_helpers/structure/validation.py +34 -6
- openai_sdk_helpers/structure/vector_search.py +132 -18
- openai_sdk_helpers/structure/web_search.py +125 -13
- openai_sdk_helpers/types.py +57 -0
- openai_sdk_helpers/utils/__init__.py +30 -1
- openai_sdk_helpers/utils/core.py +168 -34
- openai_sdk_helpers/validation.py +302 -0
- openai_sdk_helpers/vector_storage/__init__.py +21 -1
- openai_sdk_helpers/vector_storage/cleanup.py +25 -13
- openai_sdk_helpers/vector_storage/storage.py +123 -64
- openai_sdk_helpers/vector_storage/types.py +20 -19
- openai_sdk_helpers-0.0.9.dist-info/METADATA +550 -0
- openai_sdk_helpers-0.0.9.dist-info/RECORD +66 -0
- openai_sdk_helpers/streamlit_app/configuration.py +0 -324
- openai_sdk_helpers-0.0.8.dist-info/METADATA +0 -194
- openai_sdk_helpers-0.0.8.dist-info/RECORD +0 -55
- {openai_sdk_helpers-0.0.8.dist-info → openai_sdk_helpers-0.0.9.dist-info}/WHEEL +0 -0
- {openai_sdk_helpers-0.0.8.dist-info → openai_sdk_helpers-0.0.9.dist-info}/licenses/LICENSE +0 -0
|
@@ -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(
|
|
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
|
-
|
|
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
|
|
22
|
-
directory is created if it does not
|
|
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 /
|
|
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
|
-
"""
|
|
1
|
+
"""Prompt rendering utilities for template-based text generation.
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
6
|
+
Classes
|
|
7
|
+
-------
|
|
8
|
+
PromptRenderer
|
|
9
|
+
Jinja2-based template renderer for dynamic prompt generation.
|
|
10
|
+
"""
|
|
28
11
|
|
|
29
|
-
|
|
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"]
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""Core prompt rendering implementation.
|
|
2
|
+
|
|
3
|
+
This module provides the PromptRenderer class for loading and rendering
|
|
4
|
+
Jinja2 templates with context variables. Templates can be loaded from a
|
|
5
|
+
specified directory or by absolute path.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import warnings
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from dotenv import load_dotenv
|
|
15
|
+
from jinja2 import Environment, FileSystemLoader, Template
|
|
16
|
+
|
|
17
|
+
load_dotenv()
|
|
18
|
+
warnings.filterwarnings("ignore")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PromptRenderer:
|
|
22
|
+
"""Jinja2-based template renderer for dynamic prompt generation.
|
|
23
|
+
|
|
24
|
+
Loads and renders Jinja2 templates from a base directory or by absolute
|
|
25
|
+
path. The renderer supports variable substitution, template inheritance,
|
|
26
|
+
and all standard Jinja2 features for creating dynamic prompts.
|
|
27
|
+
|
|
28
|
+
Templates are loaded from a base directory (defaulting to the built-in
|
|
29
|
+
prompt package directory) or can be specified with absolute paths.
|
|
30
|
+
Autoescape is disabled by default since prompts are plain text.
|
|
31
|
+
|
|
32
|
+
Attributes
|
|
33
|
+
----------
|
|
34
|
+
base_dir : Path
|
|
35
|
+
Base directory for template loading.
|
|
36
|
+
|
|
37
|
+
Methods
|
|
38
|
+
-------
|
|
39
|
+
render(template_path, context=None)
|
|
40
|
+
Render a Jinja2 template with the given context variables.
|
|
41
|
+
|
|
42
|
+
Examples
|
|
43
|
+
--------
|
|
44
|
+
Basic template rendering with custom base directory:
|
|
45
|
+
|
|
46
|
+
>>> from pathlib import Path
|
|
47
|
+
>>> from openai_sdk_helpers.prompt import PromptRenderer
|
|
48
|
+
>>> renderer = PromptRenderer(base_dir=Path("./templates"))
|
|
49
|
+
>>> prompt = renderer.render(
|
|
50
|
+
... "greeting.jinja",
|
|
51
|
+
... context={"name": "Alice", "language": "English"}
|
|
52
|
+
... )
|
|
53
|
+
>>> print(prompt)
|
|
54
|
+
|
|
55
|
+
Using absolute path (no base_dir required):
|
|
56
|
+
|
|
57
|
+
>>> renderer = PromptRenderer()
|
|
58
|
+
>>> prompt = renderer.render(
|
|
59
|
+
... "/absolute/path/to/template.jinja",
|
|
60
|
+
... context={"name": "Bob"}
|
|
61
|
+
... )
|
|
62
|
+
|
|
63
|
+
Using built-in templates:
|
|
64
|
+
|
|
65
|
+
>>> renderer = PromptRenderer() # Uses built-in templates
|
|
66
|
+
>>> prompt = renderer.render("summarizer.jinja", context={})
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
def __init__(self, base_dir: Path | None = None) -> None:
|
|
70
|
+
"""Initialize the renderer with a Jinja2 environment.
|
|
71
|
+
|
|
72
|
+
Sets up the Jinja2 environment with a FileSystemLoader pointing to
|
|
73
|
+
the specified base directory. If no base directory is provided,
|
|
74
|
+
defaults to the built-in prompt package directory containing
|
|
75
|
+
standard templates.
|
|
76
|
+
|
|
77
|
+
Parameters
|
|
78
|
+
----------
|
|
79
|
+
base_dir : Path or None, default None
|
|
80
|
+
Base directory containing Jinja2 templates. If None, uses the
|
|
81
|
+
prompt package directory containing built-in templates.
|
|
82
|
+
|
|
83
|
+
Examples
|
|
84
|
+
--------
|
|
85
|
+
>>> from pathlib import Path
|
|
86
|
+
>>> renderer = PromptRenderer(base_dir=Path("./my_templates"))
|
|
87
|
+
>>> renderer.base_dir
|
|
88
|
+
PosixPath('.../my_templates')
|
|
89
|
+
|
|
90
|
+
>>> default_renderer = PromptRenderer()
|
|
91
|
+
>>> default_renderer.base_dir.name
|
|
92
|
+
'prompt'
|
|
93
|
+
"""
|
|
94
|
+
if base_dir is None:
|
|
95
|
+
# Defaults to the directory containing this file, which also
|
|
96
|
+
# contains the builtin prompt templates.
|
|
97
|
+
self.base_dir = Path(__file__).resolve().parent
|
|
98
|
+
else:
|
|
99
|
+
self.base_dir = base_dir
|
|
100
|
+
|
|
101
|
+
self._env = Environment(
|
|
102
|
+
loader=FileSystemLoader(str(self.base_dir)),
|
|
103
|
+
autoescape=False, # Prompts are plain text
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
def render(self, template_path: str, context: dict[str, Any] | None = None) -> str:
|
|
107
|
+
"""Render a Jinja2 template with the given context variables.
|
|
108
|
+
|
|
109
|
+
Loads the template from either an absolute path or a path relative
|
|
110
|
+
to the base directory. The template is rendered with the provided
|
|
111
|
+
context dictionary using Jinja2's template engine.
|
|
112
|
+
|
|
113
|
+
For security, relative paths are validated to prevent path traversal
|
|
114
|
+
attacks. Absolute paths are allowed but should be used with caution
|
|
115
|
+
as they bypass base directory restrictions.
|
|
116
|
+
|
|
117
|
+
Parameters
|
|
118
|
+
----------
|
|
119
|
+
template_path : str
|
|
120
|
+
Path to the template file. Can be an absolute path or relative
|
|
121
|
+
to base_dir.
|
|
122
|
+
context : dict[str, Any] or None, default None
|
|
123
|
+
Context variables to pass to the template. If None, an empty
|
|
124
|
+
dictionary is used.
|
|
125
|
+
|
|
126
|
+
Returns
|
|
127
|
+
-------
|
|
128
|
+
str
|
|
129
|
+
Fully rendered template as a string.
|
|
130
|
+
|
|
131
|
+
Raises
|
|
132
|
+
------
|
|
133
|
+
FileNotFoundError
|
|
134
|
+
If the template file does not exist at the specified path.
|
|
135
|
+
InputValidationError
|
|
136
|
+
If the path contains suspicious patterns or attempts to escape
|
|
137
|
+
the base directory.
|
|
138
|
+
|
|
139
|
+
Examples
|
|
140
|
+
--------
|
|
141
|
+
>>> renderer = PromptRenderer()
|
|
142
|
+
>>> context = {"name": "Alice", "age": 30}
|
|
143
|
+
>>> result = renderer.render("greeting.jinja", context)
|
|
144
|
+
>>> "Alice" in result
|
|
145
|
+
True
|
|
146
|
+
|
|
147
|
+
With absolute path:
|
|
148
|
+
|
|
149
|
+
>>> result = renderer.render(
|
|
150
|
+
... "/path/to/template.jinja",
|
|
151
|
+
... context={"key": "value"}
|
|
152
|
+
... )
|
|
153
|
+
"""
|
|
154
|
+
from openai_sdk_helpers.validation import validate_safe_path
|
|
155
|
+
|
|
156
|
+
path = Path(template_path)
|
|
157
|
+
if path.is_absolute():
|
|
158
|
+
# Absolute paths allowed but not validated against base_dir
|
|
159
|
+
template_path_ = path
|
|
160
|
+
else:
|
|
161
|
+
# Relative paths validated to prevent directory traversal
|
|
162
|
+
template_path_ = validate_safe_path(
|
|
163
|
+
self.base_dir / template_path,
|
|
164
|
+
base_dir=self.base_dir,
|
|
165
|
+
field_name="template_path",
|
|
166
|
+
)
|
|
167
|
+
template_path_text = template_path_.read_text()
|
|
168
|
+
template = Template(template_path_text)
|
|
169
|
+
return template.render(context or {})
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
__all__ = ["PromptRenderer"]
|
|
@@ -1,15 +1,47 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Response handling for OpenAI API interactions.
|
|
2
|
+
|
|
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.
|
|
7
|
+
|
|
8
|
+
Classes
|
|
9
|
+
-------
|
|
10
|
+
BaseResponse
|
|
11
|
+
Core response manager for OpenAI interactions with structured outputs.
|
|
12
|
+
ResponseConfiguration
|
|
13
|
+
Immutable configuration for defining request/response structures.
|
|
14
|
+
ResponseMessage
|
|
15
|
+
Single message exchanged with the OpenAI client.
|
|
16
|
+
ResponseMessages
|
|
17
|
+
Collection of messages in a response conversation.
|
|
18
|
+
ResponseToolCall
|
|
19
|
+
Container for tool call data and formatting.
|
|
20
|
+
|
|
21
|
+
Functions
|
|
22
|
+
---------
|
|
23
|
+
run_sync
|
|
24
|
+
Execute a response workflow synchronously with resource cleanup.
|
|
25
|
+
run_async
|
|
26
|
+
Execute a response workflow asynchronously with resource cleanup.
|
|
27
|
+
run_streamed
|
|
28
|
+
Execute a response workflow and return the asynchronous result.
|
|
29
|
+
attach_vector_store
|
|
30
|
+
Attach vector stores to a response's file_search tool.
|
|
31
|
+
"""
|
|
2
32
|
|
|
3
33
|
from __future__ import annotations
|
|
4
34
|
|
|
5
35
|
from .base import BaseResponse
|
|
36
|
+
from .config import ResponseConfiguration
|
|
6
37
|
from .messages import ResponseMessage, ResponseMessages
|
|
7
|
-
from .runner import
|
|
8
|
-
from .vector_store import attach_vector_store
|
|
38
|
+
from .runner import run_async, run_streamed, run_sync
|
|
9
39
|
from .tool_call import ResponseToolCall
|
|
40
|
+
from .vector_store import attach_vector_store
|
|
10
41
|
|
|
11
42
|
__all__ = [
|
|
12
43
|
"BaseResponse",
|
|
44
|
+
"ResponseConfiguration",
|
|
13
45
|
"ResponseMessage",
|
|
14
46
|
"ResponseMessages",
|
|
15
47
|
"run_sync",
|