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.
- openai_sdk_helpers/__init__.py +6 -6
- openai_sdk_helpers/agent/__init__.py +4 -2
- openai_sdk_helpers/agent/base.py +391 -106
- openai_sdk_helpers/agent/config.py +405 -44
- openai_sdk_helpers/agent/coordination.py +68 -31
- openai_sdk_helpers/agent/runner.py +29 -19
- openai_sdk_helpers/agent/search/base.py +103 -54
- openai_sdk_helpers/agent/search/vector.py +99 -68
- openai_sdk_helpers/agent/search/web.py +84 -50
- openai_sdk_helpers/agent/summarizer.py +33 -7
- openai_sdk_helpers/agent/translator.py +58 -24
- openai_sdk_helpers/agent/validation.py +35 -4
- openai_sdk_helpers/cli.py +42 -0
- openai_sdk_helpers/config.py +0 -1
- openai_sdk_helpers/environment.py +3 -2
- openai_sdk_helpers/files_api.py +35 -3
- openai_sdk_helpers/prompt/base.py +6 -0
- openai_sdk_helpers/response/__init__.py +3 -3
- openai_sdk_helpers/response/base.py +161 -22
- openai_sdk_helpers/response/config.py +50 -200
- openai_sdk_helpers/response/files.py +5 -5
- openai_sdk_helpers/response/messages.py +3 -3
- openai_sdk_helpers/response/runner.py +7 -7
- openai_sdk_helpers/response/tool_call.py +94 -4
- openai_sdk_helpers/response/vector_store.py +3 -3
- openai_sdk_helpers/streamlit_app/app.py +16 -16
- openai_sdk_helpers/streamlit_app/config.py +38 -37
- openai_sdk_helpers/streamlit_app/streamlit_web_search.py +2 -2
- openai_sdk_helpers/structure/__init__.py +6 -2
- openai_sdk_helpers/structure/agent_blueprint.py +2 -2
- openai_sdk_helpers/structure/base.py +8 -99
- openai_sdk_helpers/structure/plan/plan.py +2 -2
- openai_sdk_helpers/structure/plan/task.py +9 -9
- openai_sdk_helpers/structure/prompt.py +2 -2
- openai_sdk_helpers/structure/responses.py +15 -15
- openai_sdk_helpers/structure/summary.py +3 -3
- openai_sdk_helpers/structure/translation.py +32 -0
- openai_sdk_helpers/structure/validation.py +2 -2
- openai_sdk_helpers/structure/vector_search.py +7 -7
- openai_sdk_helpers/structure/web_search.py +6 -6
- openai_sdk_helpers/tools.py +41 -15
- openai_sdk_helpers/utils/__init__.py +19 -5
- openai_sdk_helpers/utils/instructions.py +35 -0
- openai_sdk_helpers/utils/json/__init__.py +55 -0
- openai_sdk_helpers/utils/json/base_model.py +181 -0
- openai_sdk_helpers/utils/{json_utils.py → json/data_class.py} +43 -70
- openai_sdk_helpers/utils/json/ref.py +113 -0
- openai_sdk_helpers/utils/json/utils.py +203 -0
- openai_sdk_helpers/utils/output_validation.py +21 -1
- openai_sdk_helpers/utils/path_utils.py +34 -1
- openai_sdk_helpers/utils/registry.py +194 -0
- openai_sdk_helpers/vector_storage/storage.py +10 -0
- {openai_sdk_helpers-0.2.0.dist-info → openai_sdk_helpers-0.4.0.dist-info}/METADATA +7 -7
- openai_sdk_helpers-0.4.0.dist-info/RECORD +86 -0
- openai_sdk_helpers-0.2.0.dist-info/RECORD +0 -79
- {openai_sdk_helpers-0.2.0.dist-info → openai_sdk_helpers-0.4.0.dist-info}/WHEEL +0 -0
- {openai_sdk_helpers-0.2.0.dist-info → openai_sdk_helpers-0.4.0.dist-info}/entry_points.txt +0 -0
- {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
|
|
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
|
|
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:
|
|
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 :
|
|
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:
|
|
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
|
|
167
|
-
response :
|
|
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,
|
|
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:
|
|
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 :
|
|
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) ->
|
|
230
|
-
"""Instantiate and cache the configured
|
|
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
|
-
|
|
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
|
|
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,
|
|
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,
|
|
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
|
|
14
|
+
from pydantic import ConfigDict, Field, field_validator, model_validator
|
|
15
15
|
|
|
16
|
-
from openai_sdk_helpers.response.base import
|
|
17
|
-
from openai_sdk_helpers.structure.base import
|
|
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(
|
|
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 :
|
|
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
|
|
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:
|
|
66
|
+
response: ResponseBase[StructureBase] | type[ResponseBase] | Callable | None = (
|
|
66
67
|
Field(
|
|
67
68
|
default=None,
|
|
68
69
|
description=(
|
|
69
|
-
"Configured ``
|
|
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:
|
|
134
|
-
) ->
|
|
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
|
|
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 :
|
|
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
|
-
|
|
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
|
|
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,
|
|
158
|
+
if isinstance(value, ResponseBase):
|
|
158
159
|
return value
|
|
159
|
-
if isinstance(value, type) and issubclass(value,
|
|
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
|
|
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) ->
|
|
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
|
|
209
|
+
into an active ResponseBase instance ready for chat interactions.
|
|
209
210
|
|
|
210
211
|
Returns
|
|
211
212
|
-------
|
|
212
|
-
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
349
|
+
if isinstance(raw_config, ResponseBase):
|
|
349
350
|
return StreamlitAppConfig(response=raw_config)
|
|
350
|
-
if isinstance(raw_config, type) and issubclass(raw_config,
|
|
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,
|
|
357
|
+
"APP_CONFIG must be a dict, callable, ResponseBase, or StreamlitAppConfig."
|
|
357
358
|
)
|
|
358
359
|
|
|
359
360
|
|
|
360
|
-
def _instantiate_response(candidate: object) ->
|
|
361
|
-
"""Convert a response candidate into a
|
|
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
|
-
|
|
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
|
|
381
|
+
If candidate cannot produce a ResponseBase instance.
|
|
381
382
|
|
|
382
383
|
Examples
|
|
383
384
|
--------
|
|
384
385
|
>>> response = _instantiate_response(MyResponse)
|
|
385
|
-
>>> isinstance(response,
|
|
386
|
+
>>> isinstance(response, ResponseBase)
|
|
386
387
|
True
|
|
387
388
|
"""
|
|
388
|
-
if isinstance(candidate,
|
|
389
|
+
if isinstance(candidate, ResponseBase):
|
|
389
390
|
return candidate
|
|
390
|
-
if isinstance(candidate, type) and issubclass(candidate,
|
|
391
|
-
response_cls = cast(type[
|
|
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[[],
|
|
395
|
+
response_callable = cast(Callable[[], ResponseBase[StructureBase]], candidate)
|
|
395
396
|
response = response_callable()
|
|
396
|
-
if isinstance(response,
|
|
397
|
+
if isinstance(response, ResponseBase):
|
|
397
398
|
return response
|
|
398
|
-
raise TypeError("response must be a
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
-
"
|
|
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
|
|
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(
|
|
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
|
|
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,
|
|
34
|
+
from ..utils import check_filepath, log, BaseModelJSONSerializable
|
|
36
35
|
|
|
37
|
-
T = TypeVar("T", bound="
|
|
36
|
+
T = TypeVar("T", bound="StructureBase")
|
|
38
37
|
DEFAULT_DATA_PATH: Path | None = None
|
|
39
38
|
|
|
40
39
|
|
|
41
|
-
class
|
|
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
|
|
97
|
-
>>> class MyOutput(
|
|
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
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
|
15
|
+
from ..base import StructureBase, spec_field
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
class TaskStructure(
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
|
9
|
+
from .base import StructureBase, spec_field
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class PromptStructure(
|
|
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.
|