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
@@ -0,0 +1,222 @@
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. Includes template caching for
6
+ improved performance.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import warnings
12
+ from functools import lru_cache
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+ from dotenv import load_dotenv
17
+ from jinja2 import Environment, FileSystemLoader, Template
18
+
19
+ load_dotenv()
20
+ warnings.filterwarnings("ignore")
21
+
22
+
23
+ class PromptRenderer:
24
+ """Jinja2-based template renderer for dynamic prompt generation.
25
+
26
+ Loads and renders Jinja2 templates from a base directory or by absolute
27
+ path. The renderer supports variable substitution, template inheritance,
28
+ and all standard Jinja2 features for creating dynamic prompts. Templates
29
+ are cached using LRU cache for improved performance on repeated renders.
30
+
31
+ Templates are loaded from a base directory (defaulting to the built-in
32
+ prompt package directory) or can be specified with absolute paths.
33
+ Autoescape is disabled by default since prompts are plain text.
34
+
35
+ Attributes
36
+ ----------
37
+ base_dir : Path
38
+ Base directory for template loading.
39
+
40
+ Methods
41
+ -------
42
+ render(template_path, context=None)
43
+ Render a Jinja2 template with the given context variables.
44
+ clear_cache()
45
+ Clear the template compilation cache.
46
+
47
+ Examples
48
+ --------
49
+ Basic template rendering with custom base directory:
50
+
51
+ >>> from pathlib import Path
52
+ >>> from openai_sdk_helpers.prompt import PromptRenderer
53
+ >>> renderer = PromptRenderer(base_dir=Path("./templates"))
54
+ >>> prompt = renderer.render(
55
+ ... "greeting.jinja",
56
+ ... context={"name": "Alice", "language": "English"}
57
+ ... )
58
+ >>> print(prompt)
59
+
60
+ Using absolute path (no base_dir required):
61
+
62
+ >>> renderer = PromptRenderer()
63
+ >>> prompt = renderer.render(
64
+ ... "/absolute/path/to/template.jinja",
65
+ ... context={"name": "Bob"}
66
+ ... )
67
+
68
+ Using built-in templates:
69
+
70
+ >>> renderer = PromptRenderer() # Uses built-in templates
71
+ >>> prompt = renderer.render("summarizer.jinja", context={})
72
+ """
73
+
74
+ def __init__(self, base_dir: Path | None = None) -> None:
75
+ """Initialize the renderer with a Jinja2 environment.
76
+
77
+ Sets up the Jinja2 environment with a FileSystemLoader pointing to
78
+ the specified base directory. If no base directory is provided,
79
+ defaults to the built-in prompt package directory containing
80
+ standard templates.
81
+
82
+ Parameters
83
+ ----------
84
+ base_dir : Path or None, default None
85
+ Base directory containing Jinja2 templates. If None, uses the
86
+ prompt package directory containing built-in templates.
87
+
88
+ Examples
89
+ --------
90
+ >>> from pathlib import Path
91
+ >>> renderer = PromptRenderer(base_dir=Path("./my_templates"))
92
+ >>> renderer.base_dir
93
+ PosixPath('.../my_templates')
94
+
95
+ >>> default_renderer = PromptRenderer()
96
+ >>> default_renderer.base_dir.name
97
+ 'prompt'
98
+ """
99
+ if base_dir is None:
100
+ # Defaults to the directory containing this file, which also
101
+ # contains the builtin prompt templates.
102
+ self.base_dir = Path(__file__).resolve().parent
103
+ else:
104
+ self.base_dir = base_dir
105
+
106
+ self._env = Environment(
107
+ loader=FileSystemLoader(str(self.base_dir)),
108
+ autoescape=False, # Prompts are plain text
109
+ )
110
+
111
+ @lru_cache(maxsize=128)
112
+ def _compile_template(self, template_path_str: str) -> Template:
113
+ """Compile a template by path with LRU caching.
114
+
115
+ Parameters
116
+ ----------
117
+ template_path_str : str
118
+ Absolute path to the template file.
119
+
120
+ Returns
121
+ -------
122
+ Template
123
+ Compiled Jinja2 template ready for rendering.
124
+ """
125
+ template_text = Path(template_path_str).read_text()
126
+ return Template(template_text)
127
+
128
+ def render(self, template_path: str, context: dict[str, Any] | None = None) -> str:
129
+ """Render a Jinja2 template with the given context variables.
130
+
131
+ Loads the template from either an absolute path or a path relative
132
+ to the base directory. The template is rendered with the provided
133
+ context dictionary using Jinja2's template engine. Templates are
134
+ cached for improved performance on repeated renders.
135
+
136
+ For security, relative paths are validated to prevent path traversal
137
+ attacks. Absolute paths are allowed but should be used with caution
138
+ as they bypass base directory restrictions.
139
+
140
+ Parameters
141
+ ----------
142
+ template_path : str
143
+ Path to the template file. Can be an absolute path or relative
144
+ to base_dir.
145
+ context : dict[str, Any] or None, default None
146
+ Context variables to pass to the template. If None, an empty
147
+ dictionary is used.
148
+
149
+ Returns
150
+ -------
151
+ str
152
+ Fully rendered template as a string.
153
+
154
+ Raises
155
+ ------
156
+ FileNotFoundError
157
+ If the template file does not exist at the specified path.
158
+ InputValidationError
159
+ If the path contains suspicious patterns or attempts to escape
160
+ the base directory.
161
+ TemplateNotFound
162
+ If the template cannot be loaded by Jinja2.
163
+
164
+ Examples
165
+ --------
166
+ >>> renderer = PromptRenderer()
167
+ >>> context = {"name": "Alice", "age": 30}
168
+ >>> result = renderer.render("greeting.jinja", context)
169
+ >>> "Alice" in result
170
+ True
171
+
172
+ With absolute path:
173
+
174
+ >>> result = renderer.render(
175
+ ... "/path/to/template.jinja",
176
+ ... context={"key": "value"}
177
+ ... )
178
+ """
179
+ from openai_sdk_helpers.validation import validate_safe_path
180
+
181
+ path = Path(template_path)
182
+ if path.is_absolute():
183
+ # Absolute paths allowed but not validated against base_dir
184
+ template_path_ = path
185
+ else:
186
+ # Relative paths validated to prevent directory traversal
187
+ template_path_ = validate_safe_path(
188
+ self.base_dir / template_path,
189
+ base_dir=self.base_dir,
190
+ field_name="template_path",
191
+ )
192
+
193
+ # Check if template exists and provide clear error message
194
+ if not template_path_.exists():
195
+ raise FileNotFoundError(
196
+ f"Template not found: {template_path_}. "
197
+ f"Ensure the template exists in {self.base_dir} or provide an absolute path."
198
+ )
199
+
200
+ # Cache-compile template by path (not by content)
201
+ template = self._compile_template(str(template_path_))
202
+ return template.render(context or {})
203
+
204
+ def clear_cache(self) -> None:
205
+ """Clear the template compilation cache.
206
+
207
+ Useful when templates are modified during runtime and need to be
208
+ reloaded. Call this method to force re-compilation of all templates
209
+ on next render.
210
+
211
+ Examples
212
+ --------
213
+ >>> renderer = PromptRenderer()
214
+ >>> renderer.render("template.jinja", {}) # Compiles and caches
215
+ >>> # ... modify template.jinja ...
216
+ >>> renderer.clear_cache() # Clear cache
217
+ >>> renderer.render("template.jinja", {}) # Re-compiles
218
+ """
219
+ self._compile_template.cache_clear()
220
+
221
+
222
+ __all__ = ["PromptRenderer"]
@@ -1,20 +1,55 @@
1
- """Shared response helpers for OpenAI interactions."""
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, ResponseRegistry, get_default_registry
6
37
  from .messages import ResponseMessage, ResponseMessages
7
- from .runner import run_sync, run_async, run_streamed
38
+ from .runner import run_async, run_streamed, run_sync
39
+ from .tool_call import ResponseToolCall, parse_tool_arguments
8
40
  from .vector_store import attach_vector_store
9
- from .tool_call import ResponseToolCall
10
41
 
11
42
  __all__ = [
12
43
  "BaseResponse",
44
+ "ResponseConfiguration",
45
+ "ResponseRegistry",
46
+ "get_default_registry",
13
47
  "ResponseMessage",
14
48
  "ResponseMessages",
15
49
  "run_sync",
16
50
  "run_async",
17
51
  "run_streamed",
18
52
  "ResponseToolCall",
53
+ "parse_tool_arguments",
19
54
  "attach_vector_store",
20
55
  ]