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
@@ -18,12 +18,12 @@ from dotenv import load_dotenv
18
18
 
19
19
  load_dotenv()
20
20
 
21
- from openai_sdk_helpers.response import BaseResponse, attach_vector_store
21
+ from openai_sdk_helpers.response import ResponseBase, attach_vector_store
22
22
  from openai_sdk_helpers.streamlit_app import (
23
23
  StreamlitAppConfig,
24
24
  _load_configuration,
25
25
  )
26
- from openai_sdk_helpers.structure.base import BaseStructure
26
+ from openai_sdk_helpers.structure.base import StructureBase
27
27
  from openai_sdk_helpers.utils import (
28
28
  coerce_jsonable,
29
29
  customJSONEncoder,
@@ -96,7 +96,7 @@ def _cleanup_temp_files(file_paths: list[str] | None = None) -> None:
96
96
  st.session_state["temp_file_paths"] = []
97
97
 
98
98
 
99
- def _extract_assistant_text(response: BaseResponse[Any]) -> str:
99
+ def _extract_assistant_text(response: ResponseBase[Any]) -> str:
100
100
  """Extract the latest assistant message as readable text.
101
101
 
102
102
  Searches the response's message history for the most recent assistant
@@ -104,7 +104,7 @@ def _extract_assistant_text(response: BaseResponse[Any]) -> str:
104
104
 
105
105
  Parameters
106
106
  ----------
107
- response : BaseResponse[Any]
107
+ response : ResponseBase[Any]
108
108
  Active response session with message history.
109
109
 
110
110
  Returns
@@ -153,7 +153,7 @@ def _extract_assistant_text(response: BaseResponse[Any]) -> str:
153
153
  return ""
154
154
 
155
155
 
156
- def _render_summary(result: Any, response: BaseResponse[Any]) -> str:
156
+ def _render_summary(result: Any, response: ResponseBase[Any]) -> str:
157
157
  """Generate display text for the chat transcript.
158
158
 
159
159
  Converts the response result into a human-readable format suitable
@@ -163,8 +163,8 @@ def _render_summary(result: Any, response: BaseResponse[Any]) -> str:
163
163
  Parameters
164
164
  ----------
165
165
  result : Any
166
- Parsed result from BaseResponse.run_sync.
167
- response : BaseResponse[Any]
166
+ Parsed result from ResponseBase.run_sync.
167
+ response : ResponseBase[Any]
168
168
  Response instance containing message history.
169
169
 
170
170
  Returns
@@ -177,7 +177,7 @@ def _render_summary(result: Any, response: BaseResponse[Any]) -> str:
177
177
  Falls back to extracting assistant text from message history if
178
178
  the result cannot be formatted directly.
179
179
  """
180
- if isinstance(result, BaseStructure):
180
+ if isinstance(result, StructureBase):
181
181
  return result.print()
182
182
  if isinstance(result, str):
183
183
  return result
@@ -196,7 +196,7 @@ def _render_summary(result: Any, response: BaseResponse[Any]) -> str:
196
196
  return "No response returned."
197
197
 
198
198
 
199
- def _build_raw_output(result: Any, response: BaseResponse[Any]) -> dict[str, Any]:
199
+ def _build_raw_output(result: Any, response: ResponseBase[Any]) -> dict[str, Any]:
200
200
  """Assemble raw JSON payload for the expandable transcript section.
201
201
 
202
202
  Creates a structured dictionary containing both the parsed result
@@ -206,7 +206,7 @@ def _build_raw_output(result: Any, response: BaseResponse[Any]) -> dict[str, Any
206
206
  ----------
207
207
  result : Any
208
208
  Parsed result from the response execution.
209
- response : BaseResponse[Any]
209
+ response : ResponseBase[Any]
210
210
  Response session with complete message history.
211
211
 
212
212
  Returns
@@ -226,8 +226,8 @@ def _build_raw_output(result: Any, response: BaseResponse[Any]) -> dict[str, Any
226
226
  }
227
227
 
228
228
 
229
- def _get_response_instance(config: StreamlitAppConfig) -> BaseResponse[Any]:
230
- """Instantiate and cache the configured BaseResponse.
229
+ def _get_response_instance(config: StreamlitAppConfig) -> ResponseBase[Any]:
230
+ """Instantiate and cache the configured ResponseBase.
231
231
 
232
232
  Creates a new response instance from the configuration if not already
233
233
  cached in session state. Applies vector store attachments and cleanup
@@ -240,13 +240,13 @@ def _get_response_instance(config: StreamlitAppConfig) -> BaseResponse[Any]:
240
240
 
241
241
  Returns
242
242
  -------
243
- BaseResponse[Any]
243
+ ResponseBase[Any]
244
244
  Active response instance for the current Streamlit session.
245
245
 
246
246
  Raises
247
247
  ------
248
248
  TypeError
249
- If the configured response cannot produce a BaseResponse.
249
+ If the configured response cannot produce a ResponseBase.
250
250
 
251
251
  Notes
252
252
  -----
@@ -255,7 +255,7 @@ def _get_response_instance(config: StreamlitAppConfig) -> BaseResponse[Any]:
255
255
  """
256
256
  if "response_instance" in st.session_state:
257
257
  cached = st.session_state["response_instance"]
258
- if isinstance(cached, BaseResponse):
258
+ if isinstance(cached, ResponseBase):
259
259
  return cached
260
260
 
261
261
  response = config.create_response()
@@ -291,7 +291,7 @@ def _reset_chat(close_response: bool = True) -> None:
291
291
  chat_history, response_instance, and temp_file_paths keys.
292
292
  """
293
293
  response = st.session_state.get("response_instance")
294
- if close_response and isinstance(response, BaseResponse):
294
+ if close_response and isinstance(response, ResponseBase):
295
295
  filepath = f"./data/{response.name}.{response.uuid}.json"
296
296
  response.save(filepath)
297
297
  response.close()
@@ -11,14 +11,15 @@ import importlib.util
11
11
  from pathlib import Path
12
12
  from types import ModuleType
13
13
  from typing import Callable, Sequence, cast
14
- from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
14
+ from pydantic import ConfigDict, Field, field_validator, model_validator
15
15
 
16
- from openai_sdk_helpers.response.base import BaseResponse
17
- from openai_sdk_helpers.structure.base import BaseStructure
16
+ from openai_sdk_helpers.response.base import ResponseBase
17
+ from openai_sdk_helpers.structure.base import StructureBase
18
18
  from openai_sdk_helpers.utils import ensure_list
19
+ from ..utils.json import BaseModelJSONSerializable
19
20
 
20
21
 
21
- class StreamlitAppConfig(BaseModel):
22
+ class StreamlitAppConfig(BaseModelJSONSerializable):
22
23
  """Validated configuration for Streamlit chat applications.
23
24
 
24
25
  Manages all settings required to run a configuration-driven Streamlit
@@ -28,7 +29,7 @@ class StreamlitAppConfig(BaseModel):
28
29
 
29
30
  Attributes
30
31
  ----------
31
- response : BaseResponse, type[BaseResponse], Callable, or None
32
+ response : ResponseBase, type[ResponseBase], Callable, or None
32
33
  Response handler as an instance, class, or callable factory.
33
34
  display_title : str
34
35
  Title displayed at the top of the Streamlit page.
@@ -46,7 +47,7 @@ class StreamlitAppConfig(BaseModel):
46
47
  normalized_vector_stores()
47
48
  Return configured system vector stores as a list.
48
49
  create_response()
49
- Instantiate and return the configured BaseResponse.
50
+ Instantiate and return the configured ResponseBase.
50
51
  load_app_config(config_path)
51
52
  Load, validate, and return configuration from a Python module.
52
53
 
@@ -62,11 +63,11 @@ class StreamlitAppConfig(BaseModel):
62
63
 
63
64
  model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
64
65
 
65
- response: BaseResponse[BaseStructure] | type[BaseResponse] | Callable | None = (
66
+ response: ResponseBase[StructureBase] | type[ResponseBase] | Callable | None = (
66
67
  Field(
67
68
  default=None,
68
69
  description=(
69
- "Configured ``BaseResponse`` subclass, instance, or callable that returns"
70
+ "Configured ``ResponseBase`` subclass, instance, or callable that returns"
70
71
  " a response instance."
71
72
  ),
72
73
  )
@@ -130,37 +131,37 @@ class StreamlitAppConfig(BaseModel):
130
131
  @field_validator("response")
131
132
  @classmethod
132
133
  def validate_response(
133
- cls, value: BaseResponse[BaseStructure] | type[BaseResponse] | Callable | None
134
- ) -> BaseResponse[BaseStructure] | type[BaseResponse] | Callable | None:
134
+ cls, value: ResponseBase[StructureBase] | type[ResponseBase] | Callable | None
135
+ ) -> ResponseBase[StructureBase] | type[ResponseBase] | Callable | None:
135
136
  """Validate that the response field is a valid handler source.
136
137
 
137
- Ensures the provided response can be used to create a BaseResponse
138
+ Ensures the provided response can be used to create a ResponseBase
138
139
  instance for handling chat interactions.
139
140
 
140
141
  Parameters
141
142
  ----------
142
- value : BaseResponse, type[BaseResponse], Callable, or None
143
+ value : ResponseBase, type[ResponseBase], Callable, or None
143
144
  Response handler as instance, class, or factory function.
144
145
 
145
146
  Returns
146
147
  -------
147
- BaseResponse, type[BaseResponse], Callable, or None
148
+ ResponseBase, type[ResponseBase], Callable, or None
148
149
  Validated response handler.
149
150
 
150
151
  Raises
151
152
  ------
152
153
  TypeError
153
- If value is not a BaseResponse, subclass, or callable.
154
+ If value is not a ResponseBase, subclass, or callable.
154
155
  """
155
156
  if value is None:
156
157
  return None
157
- if isinstance(value, BaseResponse):
158
+ if isinstance(value, ResponseBase):
158
159
  return value
159
- if isinstance(value, type) and issubclass(value, BaseResponse):
160
+ if isinstance(value, type) and issubclass(value, ResponseBase):
160
161
  return value
161
162
  if callable(value):
162
163
  return value
163
- raise TypeError("response must be a BaseResponse, subclass, or callable")
164
+ raise TypeError("response must be a ResponseBase, subclass, or callable")
164
165
 
165
166
  def normalized_vector_stores(self) -> list[str]:
166
167
  """Return configured system vector stores as a list.
@@ -201,21 +202,21 @@ class StreamlitAppConfig(BaseModel):
201
202
  raise ValueError("response must be provided.")
202
203
  return self
203
204
 
204
- def create_response(self) -> BaseResponse[BaseStructure]:
205
+ def create_response(self) -> ResponseBase[StructureBase]:
205
206
  """Instantiate and return the configured response handler.
206
207
 
207
208
  Converts the response field (whether class, instance, or callable)
208
- into an active BaseResponse instance ready for chat interactions.
209
+ into an active ResponseBase instance ready for chat interactions.
209
210
 
210
211
  Returns
211
212
  -------
212
- BaseResponse[BaseStructure]
213
+ ResponseBase[StructureBase]
213
214
  Active response instance for handling chat messages.
214
215
 
215
216
  Raises
216
217
  ------
217
218
  TypeError
218
- If the configured response cannot produce a BaseResponse.
219
+ If the configured response cannot produce a ResponseBase.
219
220
 
220
221
  Examples
221
222
  --------
@@ -311,7 +312,7 @@ def _extract_config(module: ModuleType) -> StreamlitAppConfig:
311
312
 
312
313
  Looks for APP_CONFIG in the module and converts it to a validated
313
314
  StreamlitAppConfig instance. Supports multiple input formats including
314
- dictionaries, BaseResponse instances, and existing config objects.
315
+ dictionaries, ResponseBase instances, and existing config objects.
315
316
 
316
317
  Parameters
317
318
  ----------
@@ -328,7 +329,7 @@ def _extract_config(module: ModuleType) -> StreamlitAppConfig:
328
329
  ValueError
329
330
  If APP_CONFIG is missing from the module.
330
331
  TypeError
331
- If APP_CONFIG is not a valid type (dict, BaseResponse, callable,
332
+ If APP_CONFIG is not a valid type (dict, ResponseBase, callable,
332
333
  or StreamlitAppConfig).
333
334
 
334
335
  Examples
@@ -345,20 +346,20 @@ def _extract_config(module: ModuleType) -> StreamlitAppConfig:
345
346
  return raw_config
346
347
  if isinstance(raw_config, dict):
347
348
  return _config_from_mapping(raw_config)
348
- if isinstance(raw_config, BaseResponse):
349
+ if isinstance(raw_config, ResponseBase):
349
350
  return StreamlitAppConfig(response=raw_config)
350
- if isinstance(raw_config, type) and issubclass(raw_config, BaseResponse):
351
+ if isinstance(raw_config, type) and issubclass(raw_config, ResponseBase):
351
352
  return StreamlitAppConfig(response=raw_config)
352
353
  if callable(raw_config):
353
354
  return StreamlitAppConfig(response=raw_config)
354
355
 
355
356
  raise TypeError(
356
- "APP_CONFIG must be a dict, callable, BaseResponse, or StreamlitAppConfig."
357
+ "APP_CONFIG must be a dict, callable, ResponseBase, or StreamlitAppConfig."
357
358
  )
358
359
 
359
360
 
360
- def _instantiate_response(candidate: object) -> BaseResponse[BaseStructure]:
361
- """Convert a response candidate into a BaseResponse instance.
361
+ def _instantiate_response(candidate: object) -> ResponseBase[StructureBase]:
362
+ """Convert a response candidate into a ResponseBase instance.
362
363
 
363
364
  Handles multiple candidate types: existing instances (returned as-is),
364
365
  classes (instantiated with no arguments), and callables (invoked to
@@ -371,31 +372,31 @@ def _instantiate_response(candidate: object) -> BaseResponse[BaseStructure]:
371
372
 
372
373
  Returns
373
374
  -------
374
- BaseResponse[BaseStructure]
375
+ ResponseBase[StructureBase]
375
376
  Active response instance ready for use.
376
377
 
377
378
  Raises
378
379
  ------
379
380
  TypeError
380
- If candidate cannot produce a BaseResponse instance.
381
+ If candidate cannot produce a ResponseBase instance.
381
382
 
382
383
  Examples
383
384
  --------
384
385
  >>> response = _instantiate_response(MyResponse)
385
- >>> isinstance(response, BaseResponse)
386
+ >>> isinstance(response, ResponseBase)
386
387
  True
387
388
  """
388
- if isinstance(candidate, BaseResponse):
389
+ if isinstance(candidate, ResponseBase):
389
390
  return candidate
390
- if isinstance(candidate, type) and issubclass(candidate, BaseResponse):
391
- response_cls = cast(type[BaseResponse[BaseStructure]], candidate)
391
+ if isinstance(candidate, type) and issubclass(candidate, ResponseBase):
392
+ response_cls = cast(type[ResponseBase[StructureBase]], candidate)
392
393
  return response_cls() # type: ignore[call-arg]
393
394
  if callable(candidate):
394
- response_callable = cast(Callable[[], BaseResponse[BaseStructure]], candidate)
395
+ response_callable = cast(Callable[[], ResponseBase[StructureBase]], candidate)
395
396
  response = response_callable()
396
- if isinstance(response, BaseResponse):
397
+ if isinstance(response, ResponseBase):
397
398
  return response
398
- raise TypeError("response must be a BaseResponse, subclass, or callable")
399
+ raise TypeError("response must be a ResponseBase, subclass, or callable")
399
400
 
400
401
 
401
402
  def _config_from_mapping(raw_config: dict) -> StreamlitAppConfig:
@@ -3,7 +3,7 @@
3
3
  import json
4
4
  from openai_sdk_helpers.agent.search.web import WebAgentSearch
5
5
  from openai_sdk_helpers.config import OpenAISettings
6
- from openai_sdk_helpers.response.base import BaseResponse
6
+ from openai_sdk_helpers.response.base import ResponseBase
7
7
  from openai_sdk_helpers.structure.web_search import WebSearchStructure
8
8
  from openai_sdk_helpers.structure.prompt import PromptStructure
9
9
  from openai_sdk_helpers.tools import ToolSpec, build_tool_definitions
@@ -11,7 +11,7 @@ from openai_sdk_helpers.utils import coerce_jsonable, customJSONEncoder
11
11
  from openai_sdk_helpers.environment import DEFAULT_MODEL
12
12
 
13
13
 
14
- class StreamlitWebSearch(BaseResponse[WebSearchStructure]):
14
+ class StreamlitWebSearch(ResponseBase[WebSearchStructure]):
15
15
  """Response tuned for a generic chat experience with structured output.
16
16
 
17
17
  Methods
@@ -7,7 +7,7 @@ generating OpenAI-compatible schema definitions.
7
7
 
8
8
  Classes
9
9
  -------
10
- BaseStructure
10
+ StructureBase
11
11
  Base class for all structured output models with schema generation.
12
12
  SchemaOptions
13
13
  Configuration options for schema generation behavior.
@@ -27,6 +27,8 @@ SummaryStructure
27
27
  Basic summary with topic breakdown.
28
28
  ExtendedSummaryStructure
29
29
  Enhanced summary with additional metadata.
30
+ TranslationStructure
31
+ Structured translation output.
30
32
  WebSearchStructure
31
33
  Web search results structure.
32
34
  WebSearchPlanStructure
@@ -74,12 +76,13 @@ from .plan import *
74
76
  from .prompt import PromptStructure
75
77
  from .responses import *
76
78
  from .summary import *
79
+ from .translation import TranslationStructure
77
80
  from .validation import ValidationResultStructure
78
81
  from .vector_search import *
79
82
  from .web_search import *
80
83
 
81
84
  __all__ = [
82
- "BaseStructure",
85
+ "StructureBase",
83
86
  "SchemaOptions",
84
87
  "spec_field",
85
88
  "AgentBlueprint",
@@ -93,6 +96,7 @@ __all__ = [
93
96
  "SummaryTopic",
94
97
  "SummaryStructure",
95
98
  "ExtendedSummaryStructure",
99
+ "TranslationStructure",
96
100
  "WebSearchStructure",
97
101
  "WebSearchPlanStructure",
98
102
  "WebSearchItemStructure",
@@ -6,12 +6,12 @@ converting them into executable plans with validation and deployment steps.
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
- from .base import BaseStructure, spec_field
9
+ from .base import StructureBase, spec_field
10
10
  from .plan import PlanStructure, TaskStructure
11
11
  from .plan.enum import AgentEnum
12
12
 
13
13
 
14
- class AgentBlueprint(BaseStructure):
14
+ class AgentBlueprint(StructureBase):
15
15
  """Capture requirements for creating a new agent.
16
16
 
17
17
  Defines the complete specification for an agent including mission,
@@ -1,6 +1,6 @@
1
1
  """Base classes for structured output models.
2
2
 
3
- This module provides the foundational BaseStructure class and utilities for
3
+ This module provides the foundational StructureBase class and utilities for
4
4
  defining Pydantic-based structured output models with OpenAI-compatible schema
5
5
  generation, validation, and serialization.
6
6
  """
@@ -12,7 +12,6 @@ import ast
12
12
  import inspect
13
13
  import json
14
14
  import logging
15
- from collections.abc import Mapping, Sequence
16
15
  from dataclasses import dataclass
17
16
  from enum import Enum
18
17
  from pathlib import Path
@@ -32,13 +31,13 @@ from openai.types.responses.response_text_config_param import ResponseTextConfig
32
31
 
33
32
  # Internal imports
34
33
 
35
- from ..utils import check_filepath, customJSONEncoder, log
34
+ from ..utils import check_filepath, log, BaseModelJSONSerializable
36
35
 
37
- T = TypeVar("T", bound="BaseStructure")
36
+ T = TypeVar("T", bound="StructureBase")
38
37
  DEFAULT_DATA_PATH: Path | None = None
39
38
 
40
39
 
41
- class BaseStructure(BaseModel):
40
+ class StructureBase(BaseModelJSONSerializable):
42
41
  """Base class for structured output models with schema generation.
43
42
 
44
43
  Provides Pydantic-based schema definition and serialization utilities
@@ -93,8 +92,8 @@ class BaseStructure(BaseModel):
93
92
  --------
94
93
  Define a custom structure:
95
94
 
96
- >>> from openai_sdk_helpers.structure import BaseStructure, spec_field
97
- >>> class MyOutput(BaseStructure):
95
+ >>> from openai_sdk_helpers.structure import StructureBase, spec_field
96
+ >>> class MyOutput(StructureBase):
98
97
  ... title: str = spec_field("title", description="The title")
99
98
  ... score: float = spec_field("score", description="Quality score")
100
99
 
@@ -454,7 +453,7 @@ class BaseStructure(BaseModel):
454
453
  schema = cls.get_schema()
455
454
  if cls.DATA_PATH is None:
456
455
  raise RuntimeError(
457
- "DATA_PATH is not set. Set BaseStructure.DATA_PATH before saving."
456
+ "DATA_PATH is not set. Set StructureBase.DATA_PATH before saving."
458
457
  )
459
458
  file_path = cls.DATA_PATH / f"{cls.__name__}_schema.json"
460
459
  check_filepath(file_path)
@@ -462,96 +461,6 @@ class BaseStructure(BaseModel):
462
461
  json.dump(schema, file_handle, indent=2, ensure_ascii=False)
463
462
  return file_path
464
463
 
465
- def to_json(self) -> dict[str, Any]:
466
- """Serialize the instance to a JSON-compatible dictionary.
467
-
468
- Converts the Pydantic model instance to a dictionary suitable for
469
- JSON serialization. Enum members are converted to their values,
470
- and nested structures are recursively processed.
471
-
472
- Returns
473
- -------
474
- dict[str, Any]
475
- Model instance serialized as a dictionary with JSON-compatible types.
476
-
477
- Examples
478
- --------
479
- >>> instance = MyStructure(title="Test", score=0.95)
480
- >>> data = instance.to_json()
481
- >>> print(json.dumps(data))
482
- """
483
-
484
- def convert(obj: Any) -> Any:
485
- if isinstance(obj, Enum):
486
- return obj.value
487
- if isinstance(obj, BaseStructure):
488
- return obj.to_json()
489
- if isinstance(obj, Mapping):
490
- return {str(k): convert(v) for k, v in obj.items()}
491
- if isinstance(obj, Sequence) and not isinstance(
492
- obj, (str, bytes, bytearray)
493
- ):
494
- return [convert(item) for item in obj]
495
- return obj
496
-
497
- payload = convert(self.model_dump())
498
-
499
- def is_list_field(field) -> bool:
500
- annotation = getattr(field, "annotation", None)
501
- if annotation is None:
502
- return False
503
-
504
- origins_to_match = {list, Sequence, tuple, set}
505
-
506
- origin = get_origin(annotation)
507
- if origin in origins_to_match or annotation in origins_to_match:
508
- return True
509
-
510
- # Check for Union types (e.g., list[str] | None)
511
- if origin is not None:
512
- # Handle Union by checking args
513
- args = get_args(annotation)
514
- return any(
515
- get_origin(arg) in origins_to_match or arg in origins_to_match
516
- for arg in args
517
- )
518
- return False
519
-
520
- for name, field in self.__class__.model_fields.items():
521
- if name not in payload:
522
- continue
523
- if not is_list_field(field):
524
- continue
525
- value = payload[name]
526
- if value is None:
527
- continue
528
- if isinstance(value, (str, bytes, bytearray)):
529
- payload[name] = [value]
530
- elif not isinstance(value, list):
531
- payload[name] = [value]
532
-
533
- return payload
534
-
535
- def to_json_file(self, filepath: str) -> str:
536
- """Write :meth:`to_json` output to ``filepath``.
537
-
538
- Parameters
539
- ----------
540
- filepath : str
541
- Destination path for the JSON file.
542
-
543
- Returns
544
- -------
545
- str
546
- Path to the written file.
547
- """
548
- check_filepath(fullfilepath=filepath)
549
- with open(file=filepath, mode="w", encoding="utf-8") as f:
550
- json.dump(
551
- self.to_json(), f, ensure_ascii=False, indent=4, cls=customJSONEncoder
552
- )
553
- return filepath
554
-
555
464
  @classmethod
556
465
  def _extract_enum_class(cls, field_type: Any) -> type[Enum] | None:
557
466
  """Extract an Enum class from a field's type annotation.
@@ -772,7 +681,7 @@ class BaseStructure(BaseModel):
772
681
  """
773
682
  return "\n".join(
774
683
  [
775
- BaseStructure.format_output(field, value=value)
684
+ StructureBase.format_output(field, value=value)
776
685
  for field, value in self.model_dump().items()
777
686
  ]
778
687
  )
@@ -14,12 +14,12 @@ from typing import Any, Awaitable, Coroutine, cast
14
14
  from collections.abc import Mapping
15
15
 
16
16
  from .enum import AgentEnum
17
- from ..base import BaseStructure, spec_field
17
+ from ..base import StructureBase, spec_field
18
18
  from .task import TaskStructure
19
19
  from .types import AgentCallable, AgentRegistry
20
20
 
21
21
 
22
- class PlanStructure(BaseStructure):
22
+ class PlanStructure(StructureBase):
23
23
  """Structured representation of an ordered list of agent tasks.
24
24
 
25
25
  Represents a complete execution plan consisting of multiple agent tasks
@@ -12,10 +12,10 @@ from typing import Literal
12
12
  from pydantic import field_validator
13
13
 
14
14
  from .enum import AgentEnum
15
- from ..base import BaseStructure, spec_field
15
+ from ..base import StructureBase, spec_field
16
16
 
17
17
 
18
- class TaskStructure(BaseStructure):
18
+ class TaskStructure(StructureBase):
19
19
  """Structured representation of a single agent task.
20
20
 
21
21
  Represents one task in an agent execution plan, including its type,
@@ -140,13 +140,13 @@ class TaskStructure(BaseStructure):
140
140
  """
141
141
  return "\n".join(
142
142
  [
143
- BaseStructure.format_output("Task type", value=self.task_type),
144
- BaseStructure.format_output("Prompt", value=self.prompt),
145
- BaseStructure.format_output("Context", value=self.context),
146
- BaseStructure.format_output("Status", value=self.status),
147
- BaseStructure.format_output("Start date", value=self.start_date),
148
- BaseStructure.format_output("End date", value=self.end_date),
149
- BaseStructure.format_output("Results", value=self.results),
143
+ StructureBase.format_output("Task type", value=self.task_type),
144
+ StructureBase.format_output("Prompt", value=self.prompt),
145
+ StructureBase.format_output("Context", value=self.context),
146
+ StructureBase.format_output("Status", value=self.status),
147
+ StructureBase.format_output("Start date", value=self.start_date),
148
+ StructureBase.format_output("End date", value=self.end_date),
149
+ StructureBase.format_output("Results", value=self.results),
150
150
  ]
151
151
  )
152
152
 
@@ -6,10 +6,10 @@ used in OpenAI API requests.
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
- from .base import BaseStructure, spec_field
9
+ from .base import StructureBase, spec_field
10
10
 
11
11
 
12
- class PromptStructure(BaseStructure):
12
+ class PromptStructure(StructureBase):
13
13
  """Structured representation of prompt text for OpenAI API requests.
14
14
 
15
15
  Simple structure containing a single prompt string with examples.