openai-sdk-helpers 0.3.0__py3-none-any.whl → 0.4.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 (57) hide show
  1. openai_sdk_helpers/__init__.py +6 -6
  2. openai_sdk_helpers/agent/__init__.py +2 -2
  3. openai_sdk_helpers/agent/base.py +231 -110
  4. openai_sdk_helpers/agent/config.py +83 -29
  5. openai_sdk_helpers/agent/coordination.py +64 -28
  6. openai_sdk_helpers/agent/runner.py +16 -15
  7. openai_sdk_helpers/agent/search/base.py +94 -45
  8. openai_sdk_helpers/agent/search/vector.py +86 -58
  9. openai_sdk_helpers/agent/search/web.py +71 -40
  10. openai_sdk_helpers/agent/summarizer.py +32 -7
  11. openai_sdk_helpers/agent/translator.py +57 -24
  12. openai_sdk_helpers/agent/validation.py +34 -4
  13. openai_sdk_helpers/cli.py +42 -0
  14. openai_sdk_helpers/config.py +0 -1
  15. openai_sdk_helpers/environment.py +3 -2
  16. openai_sdk_helpers/files_api.py +35 -3
  17. openai_sdk_helpers/prompt/base.py +6 -0
  18. openai_sdk_helpers/response/__init__.py +3 -3
  19. openai_sdk_helpers/response/base.py +142 -73
  20. openai_sdk_helpers/response/config.py +43 -51
  21. openai_sdk_helpers/response/files.py +5 -5
  22. openai_sdk_helpers/response/messages.py +3 -3
  23. openai_sdk_helpers/response/runner.py +7 -7
  24. openai_sdk_helpers/response/tool_call.py +94 -4
  25. openai_sdk_helpers/response/vector_store.py +3 -3
  26. openai_sdk_helpers/streamlit_app/app.py +16 -16
  27. openai_sdk_helpers/streamlit_app/config.py +38 -37
  28. openai_sdk_helpers/streamlit_app/streamlit_web_search.py +2 -2
  29. openai_sdk_helpers/structure/__init__.py +6 -2
  30. openai_sdk_helpers/structure/agent_blueprint.py +2 -2
  31. openai_sdk_helpers/structure/base.py +8 -99
  32. openai_sdk_helpers/structure/plan/plan.py +2 -2
  33. openai_sdk_helpers/structure/plan/task.py +9 -9
  34. openai_sdk_helpers/structure/prompt.py +2 -2
  35. openai_sdk_helpers/structure/responses.py +15 -15
  36. openai_sdk_helpers/structure/summary.py +3 -3
  37. openai_sdk_helpers/structure/translation.py +32 -0
  38. openai_sdk_helpers/structure/validation.py +2 -2
  39. openai_sdk_helpers/structure/vector_search.py +7 -7
  40. openai_sdk_helpers/structure/web_search.py +6 -6
  41. openai_sdk_helpers/tools.py +41 -15
  42. openai_sdk_helpers/utils/__init__.py +19 -5
  43. openai_sdk_helpers/utils/json/__init__.py +55 -0
  44. openai_sdk_helpers/utils/json/base_model.py +181 -0
  45. openai_sdk_helpers/utils/{json_utils.py → json/data_class.py} +33 -68
  46. openai_sdk_helpers/utils/json/ref.py +113 -0
  47. openai_sdk_helpers/utils/json/utils.py +203 -0
  48. openai_sdk_helpers/utils/output_validation.py +21 -1
  49. openai_sdk_helpers/utils/path_utils.py +34 -1
  50. openai_sdk_helpers/utils/registry.py +17 -6
  51. openai_sdk_helpers/vector_storage/storage.py +10 -0
  52. {openai_sdk_helpers-0.3.0.dist-info → openai_sdk_helpers-0.4.0.dist-info}/METADATA +7 -7
  53. openai_sdk_helpers-0.4.0.dist-info/RECORD +86 -0
  54. openai_sdk_helpers-0.3.0.dist-info/RECORD +0 -81
  55. {openai_sdk_helpers-0.3.0.dist-info → openai_sdk_helpers-0.4.0.dist-info}/WHEEL +0 -0
  56. {openai_sdk_helpers-0.3.0.dist-info → openai_sdk_helpers-0.4.0.dist-info}/entry_points.txt +0 -0
  57. {openai_sdk_helpers-0.3.0.dist-info → openai_sdk_helpers-0.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -40,9 +40,10 @@ class FilesAPIManager:
40
40
  Parameters
41
41
  ----------
42
42
  client : OpenAI
43
- OpenAI client instance for API calls.
44
- auto_track : bool, default True
45
- Automatically track uploaded files for cleanup.
43
+ An initialized OpenAI client instance used for making API calls.
44
+ auto_track : bool, default=True
45
+ If True, automatically tracks all files uploaded through the `create`
46
+ method, making them eligible for automatic deletion via `cleanup()`.
46
47
 
47
48
  Attributes
48
49
  ----------
@@ -95,6 +96,17 @@ class FilesAPIManager:
95
96
  OpenAI client instance.
96
97
  auto_track : bool, default True
97
98
  Automatically track uploaded files for cleanup.
99
+
100
+ Raises
101
+ ------
102
+ ValueError
103
+ If the client is not a valid OpenAI client.
104
+
105
+ Examples
106
+ --------
107
+ >>> from openai import OpenAI
108
+ >>> client = OpenAI()
109
+ >>> manager = FilesAPIManager(client)
98
110
  """
99
111
  self._client = client
100
112
  self._auto_track = auto_track
@@ -222,6 +234,11 @@ class FilesAPIManager:
222
234
  FileObject
223
235
  Information about the file.
224
236
 
237
+ Raises
238
+ ------
239
+ NotFoundError
240
+ If the file ID does not exist.
241
+
225
242
  Examples
226
243
  --------
227
244
  >>> file_info = manager.retrieve("file-abc123")
@@ -249,6 +266,11 @@ class FilesAPIManager:
249
266
  SyncCursorPage[FileObject]
250
267
  Page of file objects matching the criteria.
251
268
 
269
+ Raises
270
+ ------
271
+ APIError
272
+ If the OpenAI API call fails.
273
+
252
274
  Examples
253
275
  --------
254
276
  >>> # List all files
@@ -282,6 +304,11 @@ class FilesAPIManager:
282
304
  FileDeleted
283
305
  Confirmation of file deletion.
284
306
 
307
+ Raises
308
+ ------
309
+ NotFoundError
310
+ If the file ID does not exist.
311
+
285
312
  Examples
286
313
  --------
287
314
  >>> result = manager.delete("file-abc123")
@@ -310,6 +337,11 @@ class FilesAPIManager:
310
337
  bytes
311
338
  The raw bytes of the file content.
312
339
 
340
+ Raises
341
+ ------
342
+ NotFoundError
343
+ If the file ID does not exist.
344
+
313
345
  Examples
314
346
  --------
315
347
  >>> content = manager.retrieve_content("file-abc123")
@@ -32,6 +32,12 @@ class PromptRenderer:
32
32
  prompt package directory) or can be specified with absolute paths.
33
33
  Autoescape is disabled by default since prompts are plain text.
34
34
 
35
+ Parameters
36
+ ----------
37
+ base_dir : Path or None, default None
38
+ Base directory containing Jinja2 templates. If None, uses the
39
+ prompt package directory containing built-in templates.
40
+
35
41
  Attributes
36
42
  ----------
37
43
  base_dir : Path
@@ -7,7 +7,7 @@ building sophisticated AI agents with persistent conversation state.
7
7
 
8
8
  Classes
9
9
  -------
10
- BaseResponse
10
+ ResponseBase
11
11
  Core response manager for OpenAI interactions with structured outputs.
12
12
  ResponseConfiguration
13
13
  Immutable configuration for defining request/response structures.
@@ -34,7 +34,7 @@ process_files
34
34
 
35
35
  from __future__ import annotations
36
36
 
37
- from .base import BaseResponse
37
+ from .base import ResponseBase
38
38
  from .config import ResponseConfiguration, ResponseRegistry, get_default_registry
39
39
  from .files import process_files
40
40
  from .messages import ResponseMessage, ResponseMessages
@@ -43,7 +43,7 @@ from .tool_call import ResponseToolCall, parse_tool_arguments
43
43
  from .vector_store import attach_vector_store
44
44
 
45
45
  __all__ = [
46
- "BaseResponse",
46
+ "ResponseBase",
47
47
  "ResponseConfiguration",
48
48
  "ResponseRegistry",
49
49
  "get_default_registry",
@@ -1,6 +1,6 @@
1
1
  """Core response management for OpenAI API interactions.
2
2
 
3
- This module implements the BaseResponse class, which manages the complete
3
+ This module implements the ResponseBase class, which manages the complete
4
4
  lifecycle of OpenAI API interactions including input construction, tool
5
5
  execution, message history, vector store attachments, and structured output
6
6
  parsing.
@@ -44,7 +44,7 @@ from openai.types.responses.response_output_message import ResponseOutputMessage
44
44
 
45
45
  from .messages import ResponseMessage, ResponseMessages
46
46
  from ..config import OpenAISettings
47
- from ..structure import BaseStructure
47
+ from ..structure import StructureBase
48
48
  from ..types import OpenAIClient
49
49
  from ..utils import (
50
50
  check_filepath,
@@ -56,14 +56,13 @@ from ..utils import (
56
56
 
57
57
  if TYPE_CHECKING: # pragma: no cover - only for typing hints
58
58
  from openai_sdk_helpers.streamlit_app.config import StreamlitAppConfig
59
- from .config import ResponseConfiguration
60
59
 
61
- T = TypeVar("T", bound=BaseStructure)
60
+ T = TypeVar("T", bound=StructureBase)
62
61
  ToolHandler = Callable[[ResponseFunctionToolCall], str | Any]
63
- RB = TypeVar("RB", bound="BaseResponse[BaseStructure]")
62
+ RB = TypeVar("RB", bound="ResponseBase[StructureBase]")
64
63
 
65
64
 
66
- class BaseResponse(Generic[T]):
65
+ class ResponseBase(Generic[T]):
67
66
  """Manage OpenAI API interactions for structured responses.
68
67
 
69
68
  Orchestrates the complete lifecycle of OpenAI API requests including
@@ -75,14 +74,48 @@ class BaseResponse(Generic[T]):
75
74
  file attachments via vector stores, and optional parsing into typed
76
75
  structured output models. Sessions can be persisted to disk and restored.
77
76
 
77
+ Parameters
78
+ ----------
79
+ name : str
80
+ Name for this response session, used for organizing artifacts
81
+ and naming vector stores.
82
+ instructions : str
83
+ System instructions provided to the OpenAI API for context.
84
+ tools : list or None
85
+ Tool definitions for the OpenAI API request. Pass None for no tools.
86
+ output_structure : type[StructureBase] or None
87
+ Structure class used to parse tool call outputs. When provided,
88
+ the schema is automatically generated using the structure's
89
+ response_format() method. Pass None for unstructured responses.
90
+ system_vector_store : list[str] or None, default None
91
+ Optional list of vector store names to attach as system context.
92
+ data_path : Path, str, or None, default None
93
+ Optional absolute directory path for storing artifacts. If not provided,
94
+ defaults to get_data_path(class_name). Session files are saved as
95
+ data_path / uuid.json.
96
+ tool_handlers : dict[str, ToolHandler] or None, default None
97
+ Mapping from tool names to callable handlers. Each handler receives
98
+ a ResponseFunctionToolCall and returns a string or any serializable
99
+ result. Defaults to an empty dict when not provided.
100
+ openai_settings : OpenAISettings or None, default None
101
+ Fully configured OpenAI settings with API key and default model.
102
+ Required for normal operation.
103
+
78
104
  Attributes
79
105
  ----------
80
106
  uuid : UUID
81
107
  Unique identifier for this response session.
82
108
  name : str
83
109
  Lowercase class name used for path construction.
110
+ instructions_text : str
111
+ System instructions provided to the OpenAI API for context.
84
112
  messages : ResponseMessages
85
113
  Complete message history for this session.
114
+ output_structure : type[T] | None
115
+ Structure class used to parse tool call outputs, or None.
116
+ tools : list | None
117
+ Tool definitions provided to the OpenAI API, or None.
118
+
86
119
 
87
120
  Methods
88
121
  -------
@@ -107,9 +140,9 @@ class BaseResponse(Generic[T]):
107
140
 
108
141
  Examples
109
142
  --------
110
- >>> from openai_sdk_helpers import BaseResponse, OpenAISettings
143
+ >>> from openai_sdk_helpers import ResponseBase, OpenAISettings
111
144
  >>> settings = OpenAISettings(api_key="...", default_model="gpt-4")
112
- >>> response = BaseResponse(
145
+ >>> response = ResponseBase(
113
146
  ... instructions="You are a helpful assistant",
114
147
  ... tools=None,
115
148
  ... output_structure=None,
@@ -147,7 +180,7 @@ class BaseResponse(Generic[T]):
147
180
  System instructions provided to the OpenAI API for context.
148
181
  tools : list or None
149
182
  Tool definitions for the OpenAI API request. Pass None for no tools.
150
- output_structure : type[BaseStructure] or None
183
+ output_structure : type[StructureBase] or None
151
184
  Structure class used to parse tool call outputs. When provided,
152
185
  the schema is automatically generated using the structure's
153
186
  response_format() method. Pass None for unstructured responses.
@@ -175,9 +208,9 @@ class BaseResponse(Generic[T]):
175
208
 
176
209
  Examples
177
210
  --------
178
- >>> from openai_sdk_helpers import BaseResponse, OpenAISettings
211
+ >>> from openai_sdk_helpers import ResponseBase, OpenAISettings
179
212
  >>> settings = OpenAISettings(api_key="sk-...", default_model="gpt-4")
180
- >>> response = BaseResponse(
213
+ >>> response = ResponseBase(
181
214
  ... name="my_session",
182
215
  ... instructions="You are helpful",
183
216
  ... tools=None,
@@ -186,16 +219,17 @@ class BaseResponse(Generic[T]):
186
219
  ... openai_settings=settings,
187
220
  ... )
188
221
  """
189
- if tool_handlers is None:
190
- tool_handlers = {}
191
222
  if openai_settings is None:
192
223
  raise ValueError("openai_settings is required")
193
224
 
225
+ if tool_handlers is None:
226
+ tool_handlers = {}
194
227
  self._tool_handlers = tool_handlers
228
+ self.uuid = uuid.uuid4()
195
229
  self._name = name
196
230
 
197
231
  # Resolve data_path with class name appended
198
- class_name = self.__class__.__name__.lower()
232
+ class_name = self.__class__.__name__
199
233
  if data_path is not None:
200
234
  data_path_obj = Path(data_path)
201
235
  if data_path_obj.name == class_name:
@@ -205,7 +239,7 @@ class BaseResponse(Generic[T]):
205
239
  else:
206
240
  from ..environment import get_data_path
207
241
 
208
- self._data_path = get_data_path(class_name)
242
+ self._data_path = get_data_path(self.__class__.__name__)
209
243
 
210
244
  self._instructions = instructions
211
245
  self._tools = tools if tools is not None else []
@@ -227,8 +261,6 @@ class BaseResponse(Generic[T]):
227
261
  "OpenAI model is required. Set 'default_model' on OpenAISettings."
228
262
  )
229
263
 
230
- self.uuid = uuid.uuid4()
231
-
232
264
  system_content: ResponseInputMessageContentListParam = [
233
265
  ResponseInputTextParam(type="input_text", text=instructions)
234
266
  ]
@@ -274,70 +306,69 @@ class BaseResponse(Generic[T]):
274
306
  if self._data_path is not None:
275
307
  self.save()
276
308
 
277
- @classmethod
278
- def from_configuration(
279
- cls: type[RB],
280
- config: "ResponseConfiguration[Any, T]",
281
- *,
282
- openai_settings: OpenAISettings,
283
- tool_handlers: dict[str, ToolHandler] | None = None,
284
- add_output_instructions: bool = True,
285
- ) -> RB:
286
- """Construct a response instance from a configuration object.
309
+ @property
310
+ def name(self) -> str:
311
+ """Return the name of this response session.
287
312
 
288
- Parameters
289
- ----------
290
- config : ResponseConfiguration
291
- Configuration describing the response inputs, outputs, and tools.
292
- openai_settings : OpenAISettings
293
- OpenAI authentication and model configuration used for the response.
294
- tool_handlers : dict[str, ToolHandler] or None, default None
295
- Mapping of tool names to callable handlers. Defaults to an empty
296
- dictionary when not provided.
297
- add_output_instructions : bool, default True
298
- Append structured output instructions when an output structure is
299
- present.
313
+ Returns
314
+ -------
315
+ str
316
+ Name used for organizing artifacts and naming vector stores.
317
+
318
+ Examples
319
+ --------
320
+ >>> response.name
321
+ 'my_session'
322
+ """
323
+ return self._name
324
+
325
+ @property
326
+ def instructions_text(self) -> str:
327
+ """Return the system instructions for this response.
300
328
 
301
329
  Returns
302
330
  -------
303
- BaseResponse
304
- Instance of ``cls`` configured from ``config``.
331
+ str
332
+ System instructions provided to the OpenAI API.
333
+
334
+ Examples
335
+ --------
336
+ >>> response.instructions_text
337
+ 'You are a helpful assistant.'
305
338
  """
306
- handlers = tool_handlers or {}
339
+ return self._instructions
307
340
 
308
- output_instructions = ""
309
- if config.output_structure is not None and add_output_instructions:
310
- output_instructions = config.output_structure.get_prompt(
311
- add_enum_values=False
312
- )
341
+ @property
342
+ def tools(self) -> list | None:
343
+ """Return the tool definitions for this response.
313
344
 
314
- instructions = (
315
- f"{config.instructions_text}\n{output_instructions}"
316
- if output_instructions
317
- else config.instructions_text
318
- )
345
+ Returns
346
+ -------
347
+ list or None
348
+ Tool definitions provided to the OpenAI API, or None.
319
349
 
320
- return cls(
321
- name=config.name,
322
- instructions=instructions,
323
- tools=config.tools,
324
- output_structure=config.output_structure,
325
- system_vector_store=config.system_vector_store,
326
- data_path=config.data_path,
327
- tool_handlers=handlers,
328
- openai_settings=openai_settings,
329
- )
350
+ Examples
351
+ --------
352
+ >>> response.tools
353
+ []
354
+ """
355
+ return self._tools
330
356
 
331
357
  @property
332
- def name(self) -> str:
333
- """Return the name of this response session.
358
+ def output_structure(self) -> type[T] | None:
359
+ """Return the output structure class for this response.
334
360
 
335
361
  Returns
336
362
  -------
337
- str
338
- Name used for organizing artifacts and naming vector stores.
363
+ type[StructureBase] or None
364
+ Structure class used to parse tool call outputs, or None.
365
+
366
+ Examples
367
+ --------
368
+ >>> response.output_structure
369
+ None
339
370
  """
340
- return self._name
371
+ return self._output_structure
341
372
 
342
373
  def _build_input(
343
374
  self,
@@ -599,6 +630,14 @@ class BaseResponse(Generic[T]):
599
630
  T or None
600
631
  Parsed response object of type output_structure, or None.
601
632
 
633
+ Raises
634
+ ------
635
+ RuntimeError
636
+ If the API returns no output.
637
+ If a tool handler raises an exception.
638
+ ValueError
639
+ If the API invokes a tool with no registered handler.
640
+
602
641
  Examples
603
642
  --------
604
643
  >>> # Automatic type detection
@@ -671,10 +710,22 @@ class BaseResponse(Generic[T]):
671
710
  T or None
672
711
  Parsed response object of type output_structure, or None.
673
712
 
713
+ Raises
714
+ ------
715
+ RuntimeError
716
+ If the API returns no output.
717
+ If a tool handler raises an exception.
718
+ ValueError
719
+ If the API invokes a tool with no registered handler.
720
+
674
721
  Notes
675
722
  -----
676
723
  This method exists for API consistency but does not currently
677
724
  provide true streaming functionality.
725
+
726
+ Examples
727
+ --------
728
+ >>> result = response.run_streamed("Analyze these files")
678
729
  """
679
730
  return asyncio.run(
680
731
  self.run_async(
@@ -691,6 +742,10 @@ class BaseResponse(Generic[T]):
691
742
  -------
692
743
  ResponseMessage or None
693
744
  Latest tool message, or None if no tool messages exist.
745
+
746
+ Examples
747
+ --------
748
+ >>> message = response.get_last_tool_message()
694
749
  """
695
750
  return self.messages.get_last_tool_message()
696
751
 
@@ -701,6 +756,10 @@ class BaseResponse(Generic[T]):
701
756
  -------
702
757
  ResponseMessage or None
703
758
  Latest user message, or None if no user messages exist.
759
+
760
+ Examples
761
+ --------
762
+ >>> message = response.get_last_user_message()
704
763
  """
705
764
  return self.messages.get_last_user_message()
706
765
 
@@ -711,6 +770,10 @@ class BaseResponse(Generic[T]):
711
770
  -------
712
771
  ResponseMessage or None
713
772
  Latest assistant message, or None if no assistant messages exist.
773
+
774
+ Examples
775
+ --------
776
+ >>> message = response.get_last_assistant_message()
714
777
  """
715
778
  return self.messages.get_last_assistant_message()
716
779
 
@@ -777,7 +840,7 @@ class BaseResponse(Generic[T]):
777
840
  """Serialize the message history to a JSON file.
778
841
 
779
842
  Saves the complete conversation history to disk. The target path
780
- is determined by filepath parameter, or data_path if configured.
843
+ is determined by filepath parameter, or the configured data_path.
781
844
 
782
845
  Parameters
783
846
  ----------
@@ -787,8 +850,14 @@ class BaseResponse(Generic[T]):
787
850
 
788
851
  Notes
789
852
  -----
790
- If no filepath is provided and no data_path was configured during
791
- initialization, the save operation is silently skipped.
853
+ If no filepath is provided, the save operation always writes to
854
+ the session data path (data_path / name / uuid.json). The data path
855
+ is configured during initialization and defaults to get_data_path().
856
+
857
+ Raises
858
+ ------
859
+ IOError
860
+ If the file cannot be written to disk.
792
861
 
793
862
  Examples
794
863
  --------
@@ -818,12 +887,12 @@ class BaseResponse(Generic[T]):
818
887
  f"messages={len(self.messages.messages)}, data_path={self._data_path}>"
819
888
  )
820
889
 
821
- def __enter__(self) -> BaseResponse[T]:
890
+ def __enter__(self) -> ResponseBase[T]:
822
891
  """Enter the context manager for resource management.
823
892
 
824
893
  Returns
825
894
  -------
826
- BaseResponse[T]
895
+ ResponseBase[T]
827
896
  Self reference for use in with statements.
828
897
  """
829
898
  return self
@@ -857,7 +926,7 @@ class BaseResponse(Generic[T]):
857
926
 
858
927
  Examples
859
928
  --------
860
- >>> response = BaseResponse(...)
929
+ >>> response = ResponseBase(...)
861
930
  >>> try:
862
931
  ... result = response.run_sync("query")
863
932
  ... finally: