openai-sdk-helpers 0.2.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 (58) hide show
  1. openai_sdk_helpers/__init__.py +6 -6
  2. openai_sdk_helpers/agent/__init__.py +4 -2
  3. openai_sdk_helpers/agent/base.py +391 -106
  4. openai_sdk_helpers/agent/config.py +405 -44
  5. openai_sdk_helpers/agent/coordination.py +68 -31
  6. openai_sdk_helpers/agent/runner.py +29 -19
  7. openai_sdk_helpers/agent/search/base.py +103 -54
  8. openai_sdk_helpers/agent/search/vector.py +99 -68
  9. openai_sdk_helpers/agent/search/web.py +84 -50
  10. openai_sdk_helpers/agent/summarizer.py +33 -7
  11. openai_sdk_helpers/agent/translator.py +58 -24
  12. openai_sdk_helpers/agent/validation.py +35 -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 +161 -22
  20. openai_sdk_helpers/response/config.py +50 -200
  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/instructions.py +35 -0
  44. openai_sdk_helpers/utils/json/__init__.py +55 -0
  45. openai_sdk_helpers/utils/json/base_model.py +181 -0
  46. openai_sdk_helpers/utils/{json_utils.py → json/data_class.py} +43 -70
  47. openai_sdk_helpers/utils/json/ref.py +113 -0
  48. openai_sdk_helpers/utils/json/utils.py +203 -0
  49. openai_sdk_helpers/utils/output_validation.py +21 -1
  50. openai_sdk_helpers/utils/path_utils.py +34 -1
  51. openai_sdk_helpers/utils/registry.py +194 -0
  52. openai_sdk_helpers/vector_storage/storage.py +10 -0
  53. {openai_sdk_helpers-0.2.0.dist-info → openai_sdk_helpers-0.4.0.dist-info}/METADATA +7 -7
  54. openai_sdk_helpers-0.4.0.dist-info/RECORD +86 -0
  55. openai_sdk_helpers-0.2.0.dist-info/RECORD +0 -79
  56. {openai_sdk_helpers-0.2.0.dist-info → openai_sdk_helpers-0.4.0.dist-info}/WHEEL +0 -0
  57. {openai_sdk_helpers-0.2.0.dist-info → openai_sdk_helpers-0.4.0.dist-info}/entry_points.txt +0 -0
  58. {openai_sdk_helpers-0.2.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,
@@ -57,12 +57,12 @@ from ..utils import (
57
57
  if TYPE_CHECKING: # pragma: no cover - only for typing hints
58
58
  from openai_sdk_helpers.streamlit_app.config import StreamlitAppConfig
59
59
 
60
- T = TypeVar("T", bound=BaseStructure)
60
+ T = TypeVar("T", bound=StructureBase)
61
61
  ToolHandler = Callable[[ResponseFunctionToolCall], str | Any]
62
- RB = TypeVar("RB", bound="BaseResponse[BaseStructure]")
62
+ RB = TypeVar("RB", bound="ResponseBase[StructureBase]")
63
63
 
64
64
 
65
- class BaseResponse(Generic[T]):
65
+ class ResponseBase(Generic[T]):
66
66
  """Manage OpenAI API interactions for structured responses.
67
67
 
68
68
  Orchestrates the complete lifecycle of OpenAI API requests including
@@ -74,14 +74,48 @@ class BaseResponse(Generic[T]):
74
74
  file attachments via vector stores, and optional parsing into typed
75
75
  structured output models. Sessions can be persisted to disk and restored.
76
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
+
77
104
  Attributes
78
105
  ----------
79
106
  uuid : UUID
80
107
  Unique identifier for this response session.
81
108
  name : str
82
109
  Lowercase class name used for path construction.
110
+ instructions_text : str
111
+ System instructions provided to the OpenAI API for context.
83
112
  messages : ResponseMessages
84
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
+
85
119
 
86
120
  Methods
87
121
  -------
@@ -106,9 +140,9 @@ class BaseResponse(Generic[T]):
106
140
 
107
141
  Examples
108
142
  --------
109
- >>> from openai_sdk_helpers import BaseResponse, OpenAISettings
143
+ >>> from openai_sdk_helpers import ResponseBase, OpenAISettings
110
144
  >>> settings = OpenAISettings(api_key="...", default_model="gpt-4")
111
- >>> response = BaseResponse(
145
+ >>> response = ResponseBase(
112
146
  ... instructions="You are a helpful assistant",
113
147
  ... tools=None,
114
148
  ... output_structure=None,
@@ -146,7 +180,7 @@ class BaseResponse(Generic[T]):
146
180
  System instructions provided to the OpenAI API for context.
147
181
  tools : list or None
148
182
  Tool definitions for the OpenAI API request. Pass None for no tools.
149
- output_structure : type[BaseStructure] or None
183
+ output_structure : type[StructureBase] or None
150
184
  Structure class used to parse tool call outputs. When provided,
151
185
  the schema is automatically generated using the structure's
152
186
  response_format() method. Pass None for unstructured responses.
@@ -174,9 +208,9 @@ class BaseResponse(Generic[T]):
174
208
 
175
209
  Examples
176
210
  --------
177
- >>> from openai_sdk_helpers import BaseResponse, OpenAISettings
211
+ >>> from openai_sdk_helpers import ResponseBase, OpenAISettings
178
212
  >>> settings = OpenAISettings(api_key="sk-...", default_model="gpt-4")
179
- >>> response = BaseResponse(
213
+ >>> response = ResponseBase(
180
214
  ... name="my_session",
181
215
  ... instructions="You are helpful",
182
216
  ... tools=None,
@@ -185,16 +219,17 @@ class BaseResponse(Generic[T]):
185
219
  ... openai_settings=settings,
186
220
  ... )
187
221
  """
188
- if tool_handlers is None:
189
- tool_handlers = {}
190
222
  if openai_settings is None:
191
223
  raise ValueError("openai_settings is required")
192
224
 
225
+ if tool_handlers is None:
226
+ tool_handlers = {}
193
227
  self._tool_handlers = tool_handlers
228
+ self.uuid = uuid.uuid4()
194
229
  self._name = name
195
230
 
196
231
  # Resolve data_path with class name appended
197
- class_name = self.__class__.__name__.lower()
232
+ class_name = self.__class__.__name__
198
233
  if data_path is not None:
199
234
  data_path_obj = Path(data_path)
200
235
  if data_path_obj.name == class_name:
@@ -204,7 +239,7 @@ class BaseResponse(Generic[T]):
204
239
  else:
205
240
  from ..environment import get_data_path
206
241
 
207
- self._data_path = get_data_path(class_name)
242
+ self._data_path = get_data_path(self.__class__.__name__)
208
243
 
209
244
  self._instructions = instructions
210
245
  self._tools = tools if tools is not None else []
@@ -226,8 +261,6 @@ class BaseResponse(Generic[T]):
226
261
  "OpenAI model is required. Set 'default_model' on OpenAISettings."
227
262
  )
228
263
 
229
- self.uuid = uuid.uuid4()
230
-
231
264
  system_content: ResponseInputMessageContentListParam = [
232
265
  ResponseInputTextParam(type="input_text", text=instructions)
233
266
  ]
@@ -253,6 +286,21 @@ class BaseResponse(Generic[T]):
253
286
  ),
254
287
  )
255
288
 
289
+ # Add retrieval guidance to system instructions to encourage RAG usage
290
+ try:
291
+ store_names = ", ".join(system_vector_store)
292
+ except Exception:
293
+ store_names = "attached vector stores"
294
+ guidance_text = (
295
+ "Retrieval guidance: You have access to a file_search tool "
296
+ f"connected to vector store(s) {store_names}. When relevant, "
297
+ "use file_search to retrieve supporting passages before answering. "
298
+ "Cite or reference retrieved content when helpful."
299
+ )
300
+ system_content.append(
301
+ ResponseInputTextParam(type="input_text", text=guidance_text)
302
+ )
303
+
256
304
  self.messages = ResponseMessages()
257
305
  self.messages.add_system_message(content=system_content)
258
306
  if self._data_path is not None:
@@ -266,9 +314,62 @@ class BaseResponse(Generic[T]):
266
314
  -------
267
315
  str
268
316
  Name used for organizing artifacts and naming vector stores.
317
+
318
+ Examples
319
+ --------
320
+ >>> response.name
321
+ 'my_session'
269
322
  """
270
323
  return self._name
271
324
 
325
+ @property
326
+ def instructions_text(self) -> str:
327
+ """Return the system instructions for this response.
328
+
329
+ Returns
330
+ -------
331
+ str
332
+ System instructions provided to the OpenAI API.
333
+
334
+ Examples
335
+ --------
336
+ >>> response.instructions_text
337
+ 'You are a helpful assistant.'
338
+ """
339
+ return self._instructions
340
+
341
+ @property
342
+ def tools(self) -> list | None:
343
+ """Return the tool definitions for this response.
344
+
345
+ Returns
346
+ -------
347
+ list or None
348
+ Tool definitions provided to the OpenAI API, or None.
349
+
350
+ Examples
351
+ --------
352
+ >>> response.tools
353
+ []
354
+ """
355
+ return self._tools
356
+
357
+ @property
358
+ def output_structure(self) -> type[T] | None:
359
+ """Return the output structure class for this response.
360
+
361
+ Returns
362
+ -------
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
370
+ """
371
+ return self._output_structure
372
+
272
373
  def _build_input(
273
374
  self,
274
375
  content: str | list[str],
@@ -529,6 +630,14 @@ class BaseResponse(Generic[T]):
529
630
  T or None
530
631
  Parsed response object of type output_structure, or None.
531
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
+
532
641
  Examples
533
642
  --------
534
643
  >>> # Automatic type detection
@@ -601,10 +710,22 @@ class BaseResponse(Generic[T]):
601
710
  T or None
602
711
  Parsed response object of type output_structure, or None.
603
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
+
604
721
  Notes
605
722
  -----
606
723
  This method exists for API consistency but does not currently
607
724
  provide true streaming functionality.
725
+
726
+ Examples
727
+ --------
728
+ >>> result = response.run_streamed("Analyze these files")
608
729
  """
609
730
  return asyncio.run(
610
731
  self.run_async(
@@ -621,6 +742,10 @@ class BaseResponse(Generic[T]):
621
742
  -------
622
743
  ResponseMessage or None
623
744
  Latest tool message, or None if no tool messages exist.
745
+
746
+ Examples
747
+ --------
748
+ >>> message = response.get_last_tool_message()
624
749
  """
625
750
  return self.messages.get_last_tool_message()
626
751
 
@@ -631,6 +756,10 @@ class BaseResponse(Generic[T]):
631
756
  -------
632
757
  ResponseMessage or None
633
758
  Latest user message, or None if no user messages exist.
759
+
760
+ Examples
761
+ --------
762
+ >>> message = response.get_last_user_message()
634
763
  """
635
764
  return self.messages.get_last_user_message()
636
765
 
@@ -641,6 +770,10 @@ class BaseResponse(Generic[T]):
641
770
  -------
642
771
  ResponseMessage or None
643
772
  Latest assistant message, or None if no assistant messages exist.
773
+
774
+ Examples
775
+ --------
776
+ >>> message = response.get_last_assistant_message()
644
777
  """
645
778
  return self.messages.get_last_assistant_message()
646
779
 
@@ -707,7 +840,7 @@ class BaseResponse(Generic[T]):
707
840
  """Serialize the message history to a JSON file.
708
841
 
709
842
  Saves the complete conversation history to disk. The target path
710
- is determined by filepath parameter, or data_path if configured.
843
+ is determined by filepath parameter, or the configured data_path.
711
844
 
712
845
  Parameters
713
846
  ----------
@@ -717,8 +850,14 @@ class BaseResponse(Generic[T]):
717
850
 
718
851
  Notes
719
852
  -----
720
- If no filepath is provided and no data_path was configured during
721
- 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.
722
861
 
723
862
  Examples
724
863
  --------
@@ -748,12 +887,12 @@ class BaseResponse(Generic[T]):
748
887
  f"messages={len(self.messages.messages)}, data_path={self._data_path}>"
749
888
  )
750
889
 
751
- def __enter__(self) -> BaseResponse[T]:
890
+ def __enter__(self) -> ResponseBase[T]:
752
891
  """Enter the context manager for resource management.
753
892
 
754
893
  Returns
755
894
  -------
756
- BaseResponse[T]
895
+ ResponseBase[T]
757
896
  Self reference for use in with statements.
758
897
  """
759
898
  return self
@@ -787,7 +926,7 @@ class BaseResponse(Generic[T]):
787
926
 
788
927
  Examples
789
928
  --------
790
- >>> response = BaseResponse(...)
929
+ >>> response = ResponseBase(...)
791
930
  >>> try:
792
931
  ... result = response.run_sync("query")
793
932
  ... finally: