openai-sdk-helpers 0.6.1__py3-none-any.whl → 0.6.2__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/agent/__init__.py +2 -0
- openai_sdk_helpers/agent/base.py +75 -7
- openai_sdk_helpers/agent/classifier.py +244 -13
- openai_sdk_helpers/agent/configuration.py +42 -0
- openai_sdk_helpers/agent/files.py +120 -0
- openai_sdk_helpers/agent/runner.py +9 -9
- openai_sdk_helpers/agent/translator.py +2 -2
- openai_sdk_helpers/files_api.py +46 -1
- openai_sdk_helpers/prompt/classifier.jinja +20 -4
- openai_sdk_helpers/structure/__init__.py +4 -0
- openai_sdk_helpers/structure/classification.py +74 -0
- {openai_sdk_helpers-0.6.1.dist-info → openai_sdk_helpers-0.6.2.dist-info}/METADATA +1 -1
- {openai_sdk_helpers-0.6.1.dist-info → openai_sdk_helpers-0.6.2.dist-info}/RECORD +16 -15
- {openai_sdk_helpers-0.6.1.dist-info → openai_sdk_helpers-0.6.2.dist-info}/WHEEL +0 -0
- {openai_sdk_helpers-0.6.1.dist-info → openai_sdk_helpers-0.6.2.dist-info}/entry_points.txt +0 -0
- {openai_sdk_helpers-0.6.1.dist-info → openai_sdk_helpers-0.6.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -14,6 +14,7 @@ from .validator import ValidatorAgent
|
|
|
14
14
|
from .utils import run_coroutine_agent_sync
|
|
15
15
|
from .search.vector import VectorAgentSearch
|
|
16
16
|
from .search.web import WebAgentSearch
|
|
17
|
+
from .files import build_agent_input_messages
|
|
17
18
|
|
|
18
19
|
__all__ = [
|
|
19
20
|
"AgentBase",
|
|
@@ -34,4 +35,5 @@ __all__ = [
|
|
|
34
35
|
"ValidatorAgent",
|
|
35
36
|
"VectorAgentSearch",
|
|
36
37
|
"WebAgentSearch",
|
|
38
|
+
"build_agent_input_messages",
|
|
37
39
|
]
|
openai_sdk_helpers/agent/base.py
CHANGED
|
@@ -6,7 +6,7 @@ import logging
|
|
|
6
6
|
import traceback
|
|
7
7
|
import uuid
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
from typing import TYPE_CHECKING, Any, Dict, Optional, Protocol, cast
|
|
9
|
+
from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, Protocol, cast
|
|
10
10
|
|
|
11
11
|
from agents import Agent, Handoff, InputGuardrail, OutputGuardrail, Session
|
|
12
12
|
from agents.model_settings import ModelSettings
|
|
@@ -33,6 +33,7 @@ from .runner import run_async, run_sync
|
|
|
33
33
|
if TYPE_CHECKING:
|
|
34
34
|
from ..settings import OpenAISettings
|
|
35
35
|
from ..response.base import ResponseBase
|
|
36
|
+
from ..files_api import FilePurpose, FilesAPIManager
|
|
36
37
|
|
|
37
38
|
|
|
38
39
|
class AgentConfigurationProtocol(Protocol):
|
|
@@ -184,6 +185,8 @@ class AgentBase(DataclassJSONSerializable):
|
|
|
184
185
|
Return response tool handler and definition for Responses API use.
|
|
185
186
|
build_response(openai_settings, data_path=None, tool_handlers=None, system_vector_store=None)
|
|
186
187
|
Build a ResponseBase instance based on this agent.
|
|
188
|
+
build_input_messages(content, files=None, files_manager=None, file_purpose="user_data", image_detail="auto")
|
|
189
|
+
Build Agents SDK input messages with optional file attachments.
|
|
187
190
|
save_error(exc)
|
|
188
191
|
Persist error details to a file named with the agent UUID.
|
|
189
192
|
close()
|
|
@@ -467,7 +470,7 @@ class AgentBase(DataclassJSONSerializable):
|
|
|
467
470
|
|
|
468
471
|
async def run_async(
|
|
469
472
|
self,
|
|
470
|
-
input: str,
|
|
473
|
+
input: str | list[dict[str, Any]],
|
|
471
474
|
*,
|
|
472
475
|
context: Optional[Dict[str, Any]] = None,
|
|
473
476
|
output_structure: Optional[type[StructureBase]] = None,
|
|
@@ -477,8 +480,8 @@ class AgentBase(DataclassJSONSerializable):
|
|
|
477
480
|
|
|
478
481
|
Parameters
|
|
479
482
|
----------
|
|
480
|
-
input : str
|
|
481
|
-
Prompt or
|
|
483
|
+
input : str or list[dict[str, Any]]
|
|
484
|
+
Prompt text or structured input for the agent.
|
|
482
485
|
context : dict or None, default=None
|
|
483
486
|
Optional dictionary passed to the agent.
|
|
484
487
|
output_structure : type[StructureBase] or None, default=None
|
|
@@ -522,7 +525,7 @@ class AgentBase(DataclassJSONSerializable):
|
|
|
522
525
|
|
|
523
526
|
def run_sync(
|
|
524
527
|
self,
|
|
525
|
-
input: str,
|
|
528
|
+
input: str | list[dict[str, Any]],
|
|
526
529
|
*,
|
|
527
530
|
context: Optional[Dict[str, Any]] = None,
|
|
528
531
|
output_structure: Optional[type[StructureBase]] = None,
|
|
@@ -532,8 +535,8 @@ class AgentBase(DataclassJSONSerializable):
|
|
|
532
535
|
|
|
533
536
|
Parameters
|
|
534
537
|
----------
|
|
535
|
-
input : str
|
|
536
|
-
Prompt or
|
|
538
|
+
input : str or list[dict[str, Any]]
|
|
539
|
+
Prompt text or structured input for the agent.
|
|
537
540
|
context : dict or None, default=None
|
|
538
541
|
Optional dictionary passed to the agent.
|
|
539
542
|
output_structure : type[StructureBase] or None, default=None
|
|
@@ -660,6 +663,71 @@ class AgentBase(DataclassJSONSerializable):
|
|
|
660
663
|
openai_settings=openai_settings,
|
|
661
664
|
)
|
|
662
665
|
|
|
666
|
+
@staticmethod
|
|
667
|
+
def build_input_messages(
|
|
668
|
+
content: str | list[str],
|
|
669
|
+
files: str | list[str] | None = None,
|
|
670
|
+
*,
|
|
671
|
+
files_manager: FilesAPIManager | None = None,
|
|
672
|
+
openai_settings: OpenAISettings | None = None,
|
|
673
|
+
file_purpose: FilePurpose = "user_data",
|
|
674
|
+
image_detail: Literal["low", "high", "auto"] = "auto",
|
|
675
|
+
) -> list[dict[str, Any]]:
|
|
676
|
+
"""Build Agents SDK input messages with file attachments.
|
|
677
|
+
|
|
678
|
+
Parameters
|
|
679
|
+
----------
|
|
680
|
+
content : str or list[str]
|
|
681
|
+
Prompt text or list of prompt texts to send.
|
|
682
|
+
files : str, list[str], or None, default None
|
|
683
|
+
Optional file path or list of file paths. Image files are sent as
|
|
684
|
+
base64-encoded ``input_image`` entries. Document files are uploaded
|
|
685
|
+
using ``files_manager`` and sent as ``input_file`` entries.
|
|
686
|
+
files_manager : FilesAPIManager or None, default None
|
|
687
|
+
File upload helper used to create file IDs for document uploads.
|
|
688
|
+
Required when ``files`` contains non-image documents.
|
|
689
|
+
openai_settings : OpenAISettings or None, default None
|
|
690
|
+
Optional OpenAI settings used to build a FilesAPIManager when one is
|
|
691
|
+
not provided. When supplied, ``openai_settings.create_client()`` is
|
|
692
|
+
used to initialize the Files API manager.
|
|
693
|
+
file_purpose : FilePurpose, default "user_data"
|
|
694
|
+
Purpose passed to the Files API when uploading document files.
|
|
695
|
+
image_detail : {"low", "high", "auto"}, default "auto"
|
|
696
|
+
Detail hint passed along with base64-encoded image inputs.
|
|
697
|
+
|
|
698
|
+
Returns
|
|
699
|
+
-------
|
|
700
|
+
list[dict[str, Any]]
|
|
701
|
+
Agents SDK input messages that include text and optional file entries.
|
|
702
|
+
|
|
703
|
+
Raises
|
|
704
|
+
------
|
|
705
|
+
ValueError
|
|
706
|
+
If document files are provided without a ``files_manager``.
|
|
707
|
+
|
|
708
|
+
Examples
|
|
709
|
+
--------
|
|
710
|
+
>>> from openai import OpenAI
|
|
711
|
+
>>> from openai_sdk_helpers.files_api import FilesAPIManager
|
|
712
|
+
>>> client = OpenAI()
|
|
713
|
+
>>> files_manager = FilesAPIManager(client)
|
|
714
|
+
>>> messages = AgentBase.build_input_messages(
|
|
715
|
+
... "Summarize this document",
|
|
716
|
+
... files="report.pdf",
|
|
717
|
+
... files_manager=files_manager,
|
|
718
|
+
... )
|
|
719
|
+
"""
|
|
720
|
+
from .files import build_agent_input_messages
|
|
721
|
+
|
|
722
|
+
return build_agent_input_messages(
|
|
723
|
+
content=content,
|
|
724
|
+
files=files,
|
|
725
|
+
files_manager=files_manager,
|
|
726
|
+
openai_settings=openai_settings,
|
|
727
|
+
file_purpose=file_purpose,
|
|
728
|
+
image_detail=image_detail,
|
|
729
|
+
)
|
|
730
|
+
|
|
663
731
|
def _build_response_parameters(self) -> dict[str, Any]:
|
|
664
732
|
"""Build the Responses API parameter schema for this agent tool.
|
|
665
733
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
+
import threading
|
|
6
7
|
import re
|
|
7
8
|
from dataclasses import dataclass, field
|
|
8
9
|
from enum import Enum
|
|
@@ -16,6 +17,7 @@ from ..structure import (
|
|
|
16
17
|
StructureBase,
|
|
17
18
|
TaxonomyNode,
|
|
18
19
|
)
|
|
20
|
+
from ..utils import ensure_list
|
|
19
21
|
from .base import AgentBase
|
|
20
22
|
from .configuration import AgentConfiguration
|
|
21
23
|
|
|
@@ -32,8 +34,12 @@ class TaxonomyClassifierAgent(AgentBase):
|
|
|
32
34
|
|
|
33
35
|
Methods
|
|
34
36
|
-------
|
|
35
|
-
run_agent(text, taxonomy, context, max_depth)
|
|
37
|
+
run_agent(text, taxonomy, context, max_depth, session)
|
|
36
38
|
Classify text by recursively walking the taxonomy tree.
|
|
39
|
+
run_async(input, context, max_depth, confidence_threshold, single_class)
|
|
40
|
+
Classify text asynchronously using taxonomy traversal.
|
|
41
|
+
run_sync(input, context, max_depth, confidence_threshold, single_class)
|
|
42
|
+
Classify text synchronously using taxonomy traversal.
|
|
37
43
|
|
|
38
44
|
Examples
|
|
39
45
|
--------
|
|
@@ -93,9 +99,11 @@ class TaxonomyClassifierAgent(AgentBase):
|
|
|
93
99
|
text: str,
|
|
94
100
|
*,
|
|
95
101
|
context: Optional[Dict[str, Any]] = None,
|
|
102
|
+
file_ids: str | Sequence[str] | None = None,
|
|
96
103
|
max_depth: Optional[int] = None,
|
|
97
104
|
confidence_threshold: float | None = None,
|
|
98
105
|
single_class: bool = False,
|
|
106
|
+
session: Optional[Any] = None,
|
|
99
107
|
) -> ClassificationResult:
|
|
100
108
|
"""Classify ``text`` by recursively walking taxonomy levels.
|
|
101
109
|
|
|
@@ -105,12 +113,16 @@ class TaxonomyClassifierAgent(AgentBase):
|
|
|
105
113
|
Source text to classify.
|
|
106
114
|
context : dict or None, default=None
|
|
107
115
|
Additional context values to merge into the prompt.
|
|
116
|
+
file_ids : str or Sequence[str] or None, default=None
|
|
117
|
+
Optional file IDs to attach to each classification step.
|
|
108
118
|
max_depth : int or None, default=None
|
|
109
119
|
Maximum depth to traverse before stopping.
|
|
110
120
|
confidence_threshold : float or None, default=None
|
|
111
121
|
Minimum confidence required to accept a classification step.
|
|
112
122
|
single_class : bool, default=False
|
|
113
123
|
Whether to keep only the highest-priority selection per step.
|
|
124
|
+
session : Session or None, default=None
|
|
125
|
+
Optional session for maintaining conversation history across runs.
|
|
114
126
|
|
|
115
127
|
Returns
|
|
116
128
|
-------
|
|
@@ -125,15 +137,18 @@ class TaxonomyClassifierAgent(AgentBase):
|
|
|
125
137
|
True
|
|
126
138
|
"""
|
|
127
139
|
state = _TraversalState()
|
|
140
|
+
input_payload = _build_input_payload(text, file_ids)
|
|
128
141
|
await self._classify_nodes(
|
|
129
|
-
|
|
142
|
+
input_payload=input_payload,
|
|
130
143
|
nodes=list(self._root_nodes),
|
|
131
144
|
depth=0,
|
|
132
145
|
parent_path=[],
|
|
133
146
|
context=context,
|
|
147
|
+
file_ids=file_ids,
|
|
134
148
|
max_depth=max_depth,
|
|
135
149
|
confidence_threshold=confidence_threshold,
|
|
136
150
|
single_class=single_class,
|
|
151
|
+
session=session,
|
|
137
152
|
state=state,
|
|
138
153
|
)
|
|
139
154
|
|
|
@@ -149,37 +164,210 @@ class TaxonomyClassifierAgent(AgentBase):
|
|
|
149
164
|
path_nodes=state.path_nodes,
|
|
150
165
|
)
|
|
151
166
|
|
|
167
|
+
async def run_async(
|
|
168
|
+
self,
|
|
169
|
+
input: str | list[dict[str, Any]],
|
|
170
|
+
*,
|
|
171
|
+
context: Optional[Dict[str, Any]] = None,
|
|
172
|
+
output_structure: Optional[type[StructureBase]] = None,
|
|
173
|
+
session: Optional[Any] = None,
|
|
174
|
+
file_ids: str | Sequence[str] | None = None,
|
|
175
|
+
max_depth: Optional[int] = None,
|
|
176
|
+
confidence_threshold: float | None = None,
|
|
177
|
+
single_class: bool = False,
|
|
178
|
+
) -> ClassificationResult:
|
|
179
|
+
"""Classify ``input`` asynchronously with taxonomy traversal.
|
|
180
|
+
|
|
181
|
+
Parameters
|
|
182
|
+
----------
|
|
183
|
+
input : str or list[dict[str, Any]]
|
|
184
|
+
Source text to classify.
|
|
185
|
+
context : dict or None, default=None
|
|
186
|
+
Additional context values to merge into the prompt.
|
|
187
|
+
output_structure : type[StructureBase] or None, default=None
|
|
188
|
+
Unused in taxonomy traversal. Present for API compatibility.
|
|
189
|
+
session : Session or None, default=None
|
|
190
|
+
Optional session for maintaining conversation history across runs.
|
|
191
|
+
file_ids : str or Sequence[str] or None, default=None
|
|
192
|
+
Optional file IDs to attach to each classification step.
|
|
193
|
+
max_depth : int or None, default=None
|
|
194
|
+
Maximum depth to traverse before stopping.
|
|
195
|
+
confidence_threshold : float or None, default=None
|
|
196
|
+
Minimum confidence required to accept a classification step.
|
|
197
|
+
single_class : bool, default=False
|
|
198
|
+
Whether to keep only the highest-priority selection per step.
|
|
199
|
+
|
|
200
|
+
Returns
|
|
201
|
+
-------
|
|
202
|
+
ClassificationResult
|
|
203
|
+
Structured classification result describing the traversal.
|
|
204
|
+
"""
|
|
205
|
+
_ = output_structure
|
|
206
|
+
if not isinstance(input, str):
|
|
207
|
+
msg = "TaxonomyClassifierAgent run_async requires text input."
|
|
208
|
+
raise TypeError(msg)
|
|
209
|
+
kwargs: Dict[str, Any] = {
|
|
210
|
+
"context": context,
|
|
211
|
+
"file_ids": file_ids,
|
|
212
|
+
"max_depth": max_depth,
|
|
213
|
+
"confidence_threshold": confidence_threshold,
|
|
214
|
+
"single_class": single_class,
|
|
215
|
+
}
|
|
216
|
+
if session is not None:
|
|
217
|
+
kwargs["session"] = session
|
|
218
|
+
return await self.run_agent(input, **kwargs)
|
|
219
|
+
|
|
220
|
+
def run_sync(
|
|
221
|
+
self,
|
|
222
|
+
input: str | list[dict[str, Any]],
|
|
223
|
+
*,
|
|
224
|
+
context: Optional[Dict[str, Any]] = None,
|
|
225
|
+
output_structure: Optional[type[StructureBase]] = None,
|
|
226
|
+
session: Optional[Any] = None,
|
|
227
|
+
file_ids: str | Sequence[str] | None = None,
|
|
228
|
+
max_depth: Optional[int] = None,
|
|
229
|
+
confidence_threshold: float | None = None,
|
|
230
|
+
single_class: bool = False,
|
|
231
|
+
) -> ClassificationResult:
|
|
232
|
+
"""Classify ``input`` synchronously with taxonomy traversal.
|
|
233
|
+
|
|
234
|
+
Parameters
|
|
235
|
+
----------
|
|
236
|
+
input : str or list[dict[str, Any]]
|
|
237
|
+
Source text to classify.
|
|
238
|
+
context : dict or None, default=None
|
|
239
|
+
Additional context values to merge into the prompt.
|
|
240
|
+
output_structure : type[StructureBase] or None, default=None
|
|
241
|
+
Unused in taxonomy traversal. Present for API compatibility.
|
|
242
|
+
session : Session or None, default=None
|
|
243
|
+
Optional session for maintaining conversation history across runs.
|
|
244
|
+
file_ids : str or Sequence[str] or None, default=None
|
|
245
|
+
Optional file IDs to attach to each classification step.
|
|
246
|
+
max_depth : int or None, default=None
|
|
247
|
+
Maximum depth to traverse before stopping.
|
|
248
|
+
confidence_threshold : float or None, default=None
|
|
249
|
+
Minimum confidence required to accept a classification step.
|
|
250
|
+
single_class : bool, default=False
|
|
251
|
+
Whether to keep only the highest-priority selection per step.
|
|
252
|
+
|
|
253
|
+
Returns
|
|
254
|
+
-------
|
|
255
|
+
ClassificationResult
|
|
256
|
+
Structured classification result describing the traversal.
|
|
257
|
+
"""
|
|
258
|
+
_ = output_structure
|
|
259
|
+
if not isinstance(input, str):
|
|
260
|
+
msg = "TaxonomyClassifierAgent run_sync requires text input."
|
|
261
|
+
raise TypeError(msg)
|
|
262
|
+
kwargs: Dict[str, Any] = {
|
|
263
|
+
"context": context,
|
|
264
|
+
"file_ids": file_ids,
|
|
265
|
+
"max_depth": max_depth,
|
|
266
|
+
"confidence_threshold": confidence_threshold,
|
|
267
|
+
"single_class": single_class,
|
|
268
|
+
}
|
|
269
|
+
if session is not None:
|
|
270
|
+
kwargs["session"] = session
|
|
271
|
+
|
|
272
|
+
async def runner() -> ClassificationResult:
|
|
273
|
+
return await self.run_agent(input, **kwargs)
|
|
274
|
+
|
|
275
|
+
try:
|
|
276
|
+
asyncio.get_running_loop()
|
|
277
|
+
except RuntimeError:
|
|
278
|
+
return asyncio.run(runner())
|
|
279
|
+
|
|
280
|
+
result: ClassificationResult | None = None
|
|
281
|
+
error: Exception | None = None
|
|
282
|
+
|
|
283
|
+
def _thread_func() -> None:
|
|
284
|
+
nonlocal error, result
|
|
285
|
+
try:
|
|
286
|
+
result = asyncio.run(runner())
|
|
287
|
+
except Exception as exc:
|
|
288
|
+
error = exc
|
|
289
|
+
|
|
290
|
+
thread = threading.Thread(target=_thread_func)
|
|
291
|
+
thread.start()
|
|
292
|
+
thread.join()
|
|
293
|
+
|
|
294
|
+
if error is not None:
|
|
295
|
+
raise error
|
|
296
|
+
if result is None:
|
|
297
|
+
msg = "Classification did not return a result"
|
|
298
|
+
raise RuntimeError(msg)
|
|
299
|
+
return result
|
|
300
|
+
|
|
301
|
+
async def _run_step_async(
|
|
302
|
+
self,
|
|
303
|
+
*,
|
|
304
|
+
input: str | list[dict[str, Any]],
|
|
305
|
+
context: Optional[Dict[str, Any]] = None,
|
|
306
|
+
output_structure: Optional[type[StructureBase]] = None,
|
|
307
|
+
session: Optional[Any] = None,
|
|
308
|
+
) -> StructureBase:
|
|
309
|
+
"""Execute a single classification step asynchronously.
|
|
310
|
+
|
|
311
|
+
Parameters
|
|
312
|
+
----------
|
|
313
|
+
input : str or list[dict[str, Any]]
|
|
314
|
+
Prompt or structured input for the agent.
|
|
315
|
+
context : dict or None, default=None
|
|
316
|
+
Optional dictionary passed to the agent.
|
|
317
|
+
output_structure : type[StructureBase] or None, default=None
|
|
318
|
+
Optional type used to cast the final output.
|
|
319
|
+
session : Session or None, default=None
|
|
320
|
+
Optional session for maintaining conversation history across runs.
|
|
321
|
+
|
|
322
|
+
Returns
|
|
323
|
+
-------
|
|
324
|
+
StructureBase
|
|
325
|
+
Parsed result for the classification step.
|
|
326
|
+
"""
|
|
327
|
+
return await super().run_async(
|
|
328
|
+
input=input,
|
|
329
|
+
context=context,
|
|
330
|
+
output_structure=output_structure,
|
|
331
|
+
session=session,
|
|
332
|
+
)
|
|
333
|
+
|
|
152
334
|
async def _classify_nodes(
|
|
153
335
|
self,
|
|
154
336
|
*,
|
|
155
|
-
|
|
337
|
+
input_payload: str | list[dict[str, Any]],
|
|
156
338
|
nodes: list[TaxonomyNode],
|
|
157
339
|
depth: int,
|
|
158
340
|
parent_path: list[str],
|
|
159
341
|
context: Optional[Dict[str, Any]],
|
|
342
|
+
file_ids: str | Sequence[str] | None,
|
|
160
343
|
max_depth: Optional[int],
|
|
161
344
|
confidence_threshold: float | None,
|
|
162
345
|
single_class: bool,
|
|
346
|
+
session: Optional[Any],
|
|
163
347
|
state: "_TraversalState",
|
|
164
348
|
) -> None:
|
|
165
349
|
"""Classify a taxonomy level and recursively traverse children.
|
|
166
350
|
|
|
167
351
|
Parameters
|
|
168
352
|
----------
|
|
169
|
-
|
|
170
|
-
|
|
353
|
+
input_payload : str or list[dict[str, Any]]
|
|
354
|
+
Input payload used to prompt the agent.
|
|
171
355
|
nodes : list[TaxonomyNode]
|
|
172
356
|
Candidate taxonomy nodes for the current level.
|
|
173
357
|
depth : int
|
|
174
358
|
Current traversal depth.
|
|
175
359
|
context : dict or None
|
|
176
360
|
Additional context values to merge into the prompt.
|
|
361
|
+
file_ids : str or Sequence[str] or None
|
|
362
|
+
Optional file IDs attached to each classification step.
|
|
177
363
|
max_depth : int or None
|
|
178
364
|
Maximum traversal depth before stopping.
|
|
179
365
|
confidence_threshold : float or None
|
|
180
366
|
Minimum confidence required to accept a classification step.
|
|
181
367
|
single_class : bool
|
|
182
368
|
Whether to keep only the highest-priority selection per step.
|
|
369
|
+
session : Session or None
|
|
370
|
+
Optional session for maintaining conversation history across runs.
|
|
183
371
|
state : _TraversalState
|
|
184
372
|
Aggregated traversal state.
|
|
185
373
|
"""
|
|
@@ -197,10 +385,11 @@ class TaxonomyClassifierAgent(AgentBase):
|
|
|
197
385
|
context=context,
|
|
198
386
|
)
|
|
199
387
|
step_structure = _build_step_structure(list(node_paths.keys()))
|
|
200
|
-
raw_step = await self.
|
|
201
|
-
input=
|
|
388
|
+
raw_step = await self._run_step_async(
|
|
389
|
+
input=input_payload,
|
|
202
390
|
context=template_context,
|
|
203
391
|
output_structure=step_structure,
|
|
392
|
+
session=session,
|
|
204
393
|
)
|
|
205
394
|
step = _normalize_step_output(raw_step, step_structure)
|
|
206
395
|
state.path.append(step)
|
|
@@ -242,14 +431,16 @@ class TaxonomyClassifierAgent(AgentBase):
|
|
|
242
431
|
(
|
|
243
432
|
self._classify_subtree(
|
|
244
433
|
sub_agent=sub_agent,
|
|
245
|
-
|
|
434
|
+
input_payload=input_payload,
|
|
246
435
|
nodes=list(node.children),
|
|
247
436
|
depth=depth + 1,
|
|
248
437
|
parent_path=[*parent_path, node.label],
|
|
249
438
|
context=context,
|
|
439
|
+
file_ids=file_ids,
|
|
250
440
|
max_depth=max_depth,
|
|
251
441
|
confidence_threshold=confidence_threshold,
|
|
252
442
|
single_class=single_class,
|
|
443
|
+
session=session,
|
|
253
444
|
state=sub_state,
|
|
254
445
|
),
|
|
255
446
|
base_final_nodes_len,
|
|
@@ -325,21 +516,23 @@ class TaxonomyClassifierAgent(AgentBase):
|
|
|
325
516
|
model=self._model,
|
|
326
517
|
taxonomy=list(nodes),
|
|
327
518
|
)
|
|
328
|
-
sub_agent.
|
|
519
|
+
sub_agent._run_step_async = self._run_step_async
|
|
329
520
|
return sub_agent
|
|
330
521
|
|
|
331
522
|
async def _classify_subtree(
|
|
332
523
|
self,
|
|
333
524
|
*,
|
|
334
525
|
sub_agent: "TaxonomyClassifierAgent",
|
|
335
|
-
|
|
526
|
+
input_payload: str | list[dict[str, Any]],
|
|
336
527
|
nodes: list[TaxonomyNode],
|
|
337
528
|
depth: int,
|
|
338
529
|
parent_path: list[str],
|
|
339
530
|
context: Optional[Dict[str, Any]],
|
|
531
|
+
file_ids: str | Sequence[str] | None,
|
|
340
532
|
max_depth: Optional[int],
|
|
341
533
|
confidence_threshold: float | None,
|
|
342
534
|
single_class: bool,
|
|
535
|
+
session: Optional[Any],
|
|
343
536
|
state: "_TraversalState",
|
|
344
537
|
) -> "_TraversalState":
|
|
345
538
|
"""Classify a taxonomy subtree and return the traversal state.
|
|
@@ -348,8 +541,8 @@ class TaxonomyClassifierAgent(AgentBase):
|
|
|
348
541
|
----------
|
|
349
542
|
sub_agent : TaxonomyClassifierAgent
|
|
350
543
|
Sub-agent configured for the subtree traversal.
|
|
351
|
-
|
|
352
|
-
|
|
544
|
+
input_payload : str or list[dict[str, Any]]
|
|
545
|
+
Input payload used to prompt the agent.
|
|
353
546
|
nodes : list[TaxonomyNode]
|
|
354
547
|
Candidate taxonomy nodes for the subtree.
|
|
355
548
|
depth : int
|
|
@@ -358,12 +551,16 @@ class TaxonomyClassifierAgent(AgentBase):
|
|
|
358
551
|
Path segments leading to the current subtree.
|
|
359
552
|
context : dict or None
|
|
360
553
|
Additional context values to merge into the prompt.
|
|
554
|
+
file_ids : str or Sequence[str] or None
|
|
555
|
+
Optional file IDs attached to each classification step.
|
|
361
556
|
max_depth : int or None
|
|
362
557
|
Maximum traversal depth before stopping.
|
|
363
558
|
confidence_threshold : float or None
|
|
364
559
|
Minimum confidence required to accept a classification step.
|
|
365
560
|
single_class : bool
|
|
366
561
|
Whether to keep only the highest-priority selection per step.
|
|
562
|
+
session : Session or None
|
|
563
|
+
Optional session for maintaining conversation history across runs.
|
|
367
564
|
state : _TraversalState
|
|
368
565
|
Traversal state to populate for the subtree.
|
|
369
566
|
|
|
@@ -373,14 +570,16 @@ class TaxonomyClassifierAgent(AgentBase):
|
|
|
373
570
|
Populated traversal state for the subtree.
|
|
374
571
|
"""
|
|
375
572
|
await sub_agent._classify_nodes(
|
|
376
|
-
|
|
573
|
+
input_payload=input_payload,
|
|
377
574
|
nodes=nodes,
|
|
378
575
|
depth=depth,
|
|
379
576
|
parent_path=parent_path,
|
|
380
577
|
context=context,
|
|
578
|
+
file_ids=file_ids,
|
|
381
579
|
max_depth=max_depth,
|
|
382
580
|
confidence_threshold=confidence_threshold,
|
|
383
581
|
single_class=single_class,
|
|
582
|
+
session=session,
|
|
384
583
|
state=state,
|
|
385
584
|
)
|
|
386
585
|
return state
|
|
@@ -716,6 +915,38 @@ def _normalize_step_output(
|
|
|
716
915
|
return ClassificationStep.from_json(payload)
|
|
717
916
|
|
|
718
917
|
|
|
918
|
+
def _build_input_payload(
|
|
919
|
+
text: str,
|
|
920
|
+
file_ids: str | Sequence[str] | None,
|
|
921
|
+
) -> str | list[dict[str, Any]]:
|
|
922
|
+
"""Build input payloads with optional file attachments.
|
|
923
|
+
|
|
924
|
+
Parameters
|
|
925
|
+
----------
|
|
926
|
+
text : str
|
|
927
|
+
Prompt text to send to the agent.
|
|
928
|
+
file_ids : str or Sequence[str] or None
|
|
929
|
+
Optional file IDs to include as ``input_file`` attachments.
|
|
930
|
+
|
|
931
|
+
Returns
|
|
932
|
+
-------
|
|
933
|
+
str or list[dict[str, Any]]
|
|
934
|
+
Input payload suitable for the Agents SDK.
|
|
935
|
+
"""
|
|
936
|
+
normalized_file_ids = [file_id for file_id in ensure_list(file_ids) if file_id]
|
|
937
|
+
if not normalized_file_ids:
|
|
938
|
+
return text
|
|
939
|
+
attachments = [
|
|
940
|
+
{"type": "input_file", "file_id": file_id} for file_id in normalized_file_ids
|
|
941
|
+
]
|
|
942
|
+
return [
|
|
943
|
+
{
|
|
944
|
+
"role": "user",
|
|
945
|
+
"content": [{"type": "input_text", "text": text}, *attachments],
|
|
946
|
+
}
|
|
947
|
+
]
|
|
948
|
+
|
|
949
|
+
|
|
719
950
|
def _extract_enum_fields(
|
|
720
951
|
step_structure: type[StructureBase],
|
|
721
952
|
) -> dict[str, type[Enum]]:
|
|
@@ -13,6 +13,7 @@ from ..utils.json.data_class import DataclassJSONSerializable
|
|
|
13
13
|
from ..utils.registry import RegistryBase
|
|
14
14
|
from ..utils.instructions import resolve_instructions_from_path
|
|
15
15
|
from ..structure.base import StructureBase
|
|
16
|
+
from ..settings import OpenAISettings
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
class AgentRegistry(RegistryBase["AgentConfiguration"]):
|
|
@@ -152,6 +153,8 @@ class AgentConfiguration(DataclassJSONSerializable):
|
|
|
152
153
|
Resolve the prompt template path for this configuration.
|
|
153
154
|
gen_agent(run_context_wrapper)
|
|
154
155
|
Create a AgentBase instance from this configuration.
|
|
156
|
+
to_openai_settings(dotenv_path=None, **overrides)
|
|
157
|
+
Build OpenAISettings using this configuration as defaults.
|
|
155
158
|
replace(**changes)
|
|
156
159
|
Create a new AgentConfiguration with specified fields replaced.
|
|
157
160
|
to_json()
|
|
@@ -272,6 +275,45 @@ class AgentConfiguration(DataclassJSONSerializable):
|
|
|
272
275
|
"""Resolve instructions from string or file path."""
|
|
273
276
|
return resolve_instructions_from_path(self.instructions)
|
|
274
277
|
|
|
278
|
+
def to_openai_settings(
|
|
279
|
+
self, *, dotenv_path: Path | None = None, **overrides: Any
|
|
280
|
+
) -> OpenAISettings:
|
|
281
|
+
"""Build OpenAI settings using this configuration as defaults.
|
|
282
|
+
|
|
283
|
+
Parameters
|
|
284
|
+
----------
|
|
285
|
+
dotenv_path : Path or None, optional
|
|
286
|
+
Optional dotenv file path for loading environment variables.
|
|
287
|
+
overrides : Any
|
|
288
|
+
Keyword overrides applied on top of environment values. Use this
|
|
289
|
+
to supply API credentials and override defaults.
|
|
290
|
+
|
|
291
|
+
Returns
|
|
292
|
+
-------
|
|
293
|
+
OpenAISettings
|
|
294
|
+
OpenAI settings instance with defaults derived from this
|
|
295
|
+
configuration.
|
|
296
|
+
|
|
297
|
+
Raises
|
|
298
|
+
------
|
|
299
|
+
ValueError
|
|
300
|
+
If no API key is supplied via overrides or environment variables.
|
|
301
|
+
|
|
302
|
+
Examples
|
|
303
|
+
--------
|
|
304
|
+
>>> configuration = AgentConfiguration(
|
|
305
|
+
... name="summarizer",
|
|
306
|
+
... instructions="Summarize text",
|
|
307
|
+
... model="gpt-4o-mini",
|
|
308
|
+
... )
|
|
309
|
+
>>> settings = configuration.to_openai_settings(api_key="sk-...")
|
|
310
|
+
>>> # Or rely on environment variables like OPENAI_API_KEY
|
|
311
|
+
>>> settings = configuration.to_openai_settings()
|
|
312
|
+
"""
|
|
313
|
+
if self.model and "default_model" not in overrides:
|
|
314
|
+
overrides["default_model"] = self.model
|
|
315
|
+
return OpenAISettings.from_env(dotenv_path=dotenv_path, **overrides)
|
|
316
|
+
|
|
275
317
|
def resolve_prompt_path(self, prompt_dir: Path | None = None) -> Path | None:
|
|
276
318
|
"""Resolve the prompt template path for this configuration.
|
|
277
319
|
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""File attachment helpers for the Agents SDK."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Literal
|
|
6
|
+
|
|
7
|
+
from ..files_api import FilePurpose, FilesAPIManager
|
|
8
|
+
from ..settings import OpenAISettings
|
|
9
|
+
from ..utils import create_image_data_url, ensure_list, is_image_file
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def build_agent_input_messages(
|
|
13
|
+
content: str | list[str],
|
|
14
|
+
files: str | list[str] | None = None,
|
|
15
|
+
*,
|
|
16
|
+
files_manager: FilesAPIManager | None = None,
|
|
17
|
+
openai_settings: OpenAISettings | None = None,
|
|
18
|
+
file_purpose: FilePurpose = "user_data",
|
|
19
|
+
image_detail: Literal["low", "high", "auto"] = "auto",
|
|
20
|
+
) -> list[dict[str, Any]]:
|
|
21
|
+
"""Build Agents SDK input messages with file attachments.
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
content : str or list[str]
|
|
26
|
+
Prompt text or list of prompt texts to send.
|
|
27
|
+
files : str, list[str], or None, default None
|
|
28
|
+
Optional file path or list of file paths. Image files are sent as
|
|
29
|
+
base64-encoded ``input_image`` entries. Document files are uploaded
|
|
30
|
+
using ``files_manager`` and sent as ``input_file`` entries.
|
|
31
|
+
files_manager : FilesAPIManager or None, default None
|
|
32
|
+
File upload helper used to create file IDs for document uploads.
|
|
33
|
+
Required when ``files`` contains non-image documents.
|
|
34
|
+
openai_settings : OpenAISettings or None, default None
|
|
35
|
+
Optional OpenAI settings used to build a FilesAPIManager when one is
|
|
36
|
+
not provided. When supplied, ``openai_settings.create_client()`` is
|
|
37
|
+
used to initialize the Files API manager.
|
|
38
|
+
file_purpose : FilePurpose, default "user_data"
|
|
39
|
+
Purpose passed to the Files API when uploading document files.
|
|
40
|
+
image_detail : {"low", "high", "auto"}, default "auto"
|
|
41
|
+
Detail hint passed along with base64-encoded image inputs.
|
|
42
|
+
|
|
43
|
+
Returns
|
|
44
|
+
-------
|
|
45
|
+
list[dict[str, Any]]
|
|
46
|
+
Agents SDK input messages that include text and optional file entries.
|
|
47
|
+
|
|
48
|
+
Raises
|
|
49
|
+
------
|
|
50
|
+
ValueError
|
|
51
|
+
If document files are provided without a ``files_manager``.
|
|
52
|
+
|
|
53
|
+
Examples
|
|
54
|
+
--------
|
|
55
|
+
>>> from openai import OpenAI
|
|
56
|
+
>>> from openai_sdk_helpers.files_api import FilesAPIManager
|
|
57
|
+
>>> from openai_sdk_helpers.agent.files import build_agent_input_messages
|
|
58
|
+
>>> client = OpenAI()
|
|
59
|
+
>>> files_manager = FilesAPIManager(client)
|
|
60
|
+
>>> messages = build_agent_input_messages(
|
|
61
|
+
... "Summarize this document",
|
|
62
|
+
... files="report.pdf",
|
|
63
|
+
... files_manager=files_manager,
|
|
64
|
+
... )
|
|
65
|
+
"""
|
|
66
|
+
contents = ensure_list(content)
|
|
67
|
+
all_files = ensure_list(files)
|
|
68
|
+
|
|
69
|
+
image_files: list[str] = []
|
|
70
|
+
document_files: list[str] = []
|
|
71
|
+
for file_path in all_files:
|
|
72
|
+
if is_image_file(file_path):
|
|
73
|
+
image_files.append(file_path)
|
|
74
|
+
else:
|
|
75
|
+
document_files.append(file_path)
|
|
76
|
+
|
|
77
|
+
attachments: list[dict[str, Any]] = []
|
|
78
|
+
|
|
79
|
+
if document_files:
|
|
80
|
+
if files_manager is None and openai_settings is not None:
|
|
81
|
+
files_manager = FilesAPIManager(openai_settings.create_client())
|
|
82
|
+
if files_manager is None:
|
|
83
|
+
raise ValueError(
|
|
84
|
+
"files_manager is required to upload document files for agent input."
|
|
85
|
+
)
|
|
86
|
+
expires_after = 86400 if file_purpose == "user_data" else None
|
|
87
|
+
if hasattr(files_manager, "batch_upload"):
|
|
88
|
+
uploaded_files = files_manager.batch_upload(
|
|
89
|
+
document_files,
|
|
90
|
+
purpose=file_purpose,
|
|
91
|
+
expires_after=expires_after,
|
|
92
|
+
)
|
|
93
|
+
else:
|
|
94
|
+
uploaded_files = [
|
|
95
|
+
files_manager.create(
|
|
96
|
+
file_path, purpose=file_purpose, expires_after=expires_after
|
|
97
|
+
)
|
|
98
|
+
for file_path in document_files
|
|
99
|
+
]
|
|
100
|
+
for uploaded_file in uploaded_files:
|
|
101
|
+
attachments.append({"type": "input_file", "file_id": uploaded_file.id})
|
|
102
|
+
|
|
103
|
+
for image_path in image_files:
|
|
104
|
+
image_url, detail = create_image_data_url(image_path, detail=image_detail)
|
|
105
|
+
attachments.append(
|
|
106
|
+
{"type": "input_image", "image_url": image_url, "detail": detail}
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
messages: list[dict[str, Any]] = []
|
|
110
|
+
for index, raw_content in enumerate(contents):
|
|
111
|
+
text = raw_content.strip()
|
|
112
|
+
content_items: list[dict[str, Any]] = [{"type": "input_text", "text": text}]
|
|
113
|
+
if index == 0:
|
|
114
|
+
content_items.extend(attachments)
|
|
115
|
+
messages.append({"role": "user", "content": content_items})
|
|
116
|
+
|
|
117
|
+
return messages
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
__all__ = ["build_agent_input_messages"]
|
|
@@ -7,7 +7,7 @@ signatures whether they need asynchronous or synchronous results.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
-
from typing import Any, Dict, Optional
|
|
10
|
+
from typing import Any, Dict, Optional, cast
|
|
11
11
|
|
|
12
12
|
from agents import Agent, RunResult, Runner, Session
|
|
13
13
|
|
|
@@ -17,7 +17,7 @@ from ..structure.base import StructureBase
|
|
|
17
17
|
|
|
18
18
|
async def run_async(
|
|
19
19
|
agent: Agent,
|
|
20
|
-
input: str,
|
|
20
|
+
input: str | list[dict[str, Any]],
|
|
21
21
|
*,
|
|
22
22
|
context: Optional[Dict[str, Any]] = None,
|
|
23
23
|
output_structure: Optional[type[StructureBase]] = None,
|
|
@@ -29,8 +29,8 @@ async def run_async(
|
|
|
29
29
|
----------
|
|
30
30
|
agent : Agent
|
|
31
31
|
Configured agent instance to execute.
|
|
32
|
-
input : str
|
|
33
|
-
Prompt or
|
|
32
|
+
input : str or list[dict[str, Any]]
|
|
33
|
+
Prompt text or structured input for the agent.
|
|
34
34
|
context : dict or None, default=None
|
|
35
35
|
Optional context dictionary passed to the agent.
|
|
36
36
|
output_structure : type[StructureBase] or None, default=None
|
|
@@ -53,7 +53,7 @@ async def run_async(
|
|
|
53
53
|
... return result
|
|
54
54
|
>>> asyncio.run(example()) # doctest: +SKIP
|
|
55
55
|
"""
|
|
56
|
-
result = await Runner.run(agent, input, context=context, session=session)
|
|
56
|
+
result = await Runner.run(agent, cast(Any, input), context=context, session=session)
|
|
57
57
|
if output_structure is not None:
|
|
58
58
|
return result.final_output_as(output_structure)
|
|
59
59
|
return result
|
|
@@ -61,7 +61,7 @@ async def run_async(
|
|
|
61
61
|
|
|
62
62
|
def run_sync(
|
|
63
63
|
agent: Agent,
|
|
64
|
-
input: str,
|
|
64
|
+
input: str | list[dict[str, Any]],
|
|
65
65
|
*,
|
|
66
66
|
context: Optional[Dict[str, Any]] = None,
|
|
67
67
|
output_structure: Optional[type[StructureBase]] = None,
|
|
@@ -77,8 +77,8 @@ def run_sync(
|
|
|
77
77
|
----------
|
|
78
78
|
agent : Agent
|
|
79
79
|
Configured agent instance to execute.
|
|
80
|
-
input : str
|
|
81
|
-
Prompt or
|
|
80
|
+
input : str or list[dict[str, Any]]
|
|
81
|
+
Prompt text or structured input for the agent.
|
|
82
82
|
context : dict or None, default=None
|
|
83
83
|
Optional context dictionary passed to the agent.
|
|
84
84
|
output_structure : type[StructureBase] or None, default=None
|
|
@@ -102,7 +102,7 @@ def run_sync(
|
|
|
102
102
|
>>> agent = Agent(name="test", instructions="test", model="gpt-4o-mini")
|
|
103
103
|
>>> result = run_sync(agent, "What is 2+2?") # doctest: +SKIP
|
|
104
104
|
"""
|
|
105
|
-
coro = Runner.run(agent, input, context=context, session=session)
|
|
105
|
+
coro = Runner.run(agent, cast(Any, input), context=context, session=session)
|
|
106
106
|
result: RunResult = run_coroutine_with_fallback(coro)
|
|
107
107
|
if output_structure is not None:
|
|
108
108
|
return result.final_output_as(output_structure)
|
|
@@ -138,7 +138,7 @@ class TranslatorAgent(AgentBase):
|
|
|
138
138
|
|
|
139
139
|
def run_sync(
|
|
140
140
|
self,
|
|
141
|
-
input: str,
|
|
141
|
+
input: str | list[dict[str, Any]],
|
|
142
142
|
*,
|
|
143
143
|
context: Optional[Dict[str, Any]] = None,
|
|
144
144
|
output_structure: Optional[type[StructureBase]] = None,
|
|
@@ -149,7 +149,7 @@ class TranslatorAgent(AgentBase):
|
|
|
149
149
|
|
|
150
150
|
Parameters
|
|
151
151
|
----------
|
|
152
|
-
input : str
|
|
152
|
+
input : str or list[dict[str, Any]]
|
|
153
153
|
Source content to translate.
|
|
154
154
|
context : dict or None, default=None
|
|
155
155
|
Additional context values to merge into the prompt.
|
openai_sdk_helpers/files_api.py
CHANGED
|
@@ -12,7 +12,7 @@ from __future__ import annotations
|
|
|
12
12
|
|
|
13
13
|
import logging
|
|
14
14
|
from pathlib import Path
|
|
15
|
-
from typing import Any, BinaryIO, Literal, cast
|
|
15
|
+
from typing import Any, BinaryIO, Literal, Sequence, cast
|
|
16
16
|
|
|
17
17
|
from openai import OpenAI, NOT_GIVEN
|
|
18
18
|
from openai.types import FileDeleted, FileObject
|
|
@@ -62,6 +62,8 @@ class FilesAPIManager:
|
|
|
62
62
|
Delete a specific file.
|
|
63
63
|
retrieve_content(file_id)
|
|
64
64
|
Download file content.
|
|
65
|
+
batch_upload(files, purpose, track, expires_after)
|
|
66
|
+
Upload multiple files to the Files API.
|
|
65
67
|
cleanup()
|
|
66
68
|
Delete all tracked files.
|
|
67
69
|
|
|
@@ -350,6 +352,49 @@ class FilesAPIManager:
|
|
|
350
352
|
"""
|
|
351
353
|
return self._client.files.content(file_id).read()
|
|
352
354
|
|
|
355
|
+
def batch_upload(
|
|
356
|
+
self,
|
|
357
|
+
files: Sequence[BinaryIO | Path | str],
|
|
358
|
+
purpose: FilePurpose,
|
|
359
|
+
track: bool | None = None,
|
|
360
|
+
expires_after: int | None = None,
|
|
361
|
+
) -> list[FileObject]:
|
|
362
|
+
"""Upload multiple files to the OpenAI Files API.
|
|
363
|
+
|
|
364
|
+
Parameters
|
|
365
|
+
----------
|
|
366
|
+
files : Sequence[BinaryIO | Path | str]
|
|
367
|
+
File-like objects or file paths to upload.
|
|
368
|
+
purpose : FilePurpose
|
|
369
|
+
The intended purpose of the uploaded files.
|
|
370
|
+
track : bool or None, default None
|
|
371
|
+
Override auto_track for these uploads. If None, uses instance setting.
|
|
372
|
+
expires_after : int or None, default None
|
|
373
|
+
Number of seconds after which files expire. See ``create`` for details.
|
|
374
|
+
|
|
375
|
+
Returns
|
|
376
|
+
-------
|
|
377
|
+
list[FileObject]
|
|
378
|
+
Uploaded file objects in the same order as ``files``.
|
|
379
|
+
|
|
380
|
+
Examples
|
|
381
|
+
--------
|
|
382
|
+
>>> files = ["doc1.pdf", "doc2.pdf"]
|
|
383
|
+
>>> uploaded = manager.batch_upload(files, purpose="user_data")
|
|
384
|
+
>>> [file.id for file in uploaded]
|
|
385
|
+
"""
|
|
386
|
+
if not files:
|
|
387
|
+
return []
|
|
388
|
+
return [
|
|
389
|
+
self.create(
|
|
390
|
+
file_path,
|
|
391
|
+
purpose=purpose,
|
|
392
|
+
track=track,
|
|
393
|
+
expires_after=expires_after,
|
|
394
|
+
)
|
|
395
|
+
for file_path in files
|
|
396
|
+
]
|
|
397
|
+
|
|
353
398
|
def cleanup(self) -> dict[str, bool]:
|
|
354
399
|
"""Delete all tracked files.
|
|
355
400
|
|
|
@@ -5,12 +5,28 @@ Instructions:
|
|
|
5
5
|
- Populate selected_nodes as a list of taxonomy node ids for multi-class matches.
|
|
6
6
|
- Use selected_node when a single best match is appropriate.
|
|
7
7
|
- Provide a confidence score between 0 and 1 for the selections; higher means more certain.
|
|
8
|
+
- Interpret confidence as:
|
|
9
|
+
- 0.90–1.00: explicit lexical match.
|
|
10
|
+
- 0.70–0.89: strong semantic alignment.
|
|
11
|
+
- 0.40–0.69: weak or ambiguous alignment.
|
|
12
|
+
- <0.40: low-confidence inference.
|
|
8
13
|
- Use only taxonomy identifiers from the candidate list for any selections.
|
|
9
14
|
- Use the stop_reason enum values only: "continue", "stop", "no_match", "max_depth", "no_children".
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
15
|
+
- Stop reason semantics:
|
|
16
|
+
- continue: valid match exists and deeper traversal is required.
|
|
17
|
+
- stop: low confidence, terminate to avoid false precision.
|
|
18
|
+
- no_match: no semantic fit in candidates.
|
|
19
|
+
- max_depth: taxonomy depth limit reached.
|
|
20
|
+
- no_children: matched node has no children.
|
|
21
|
+
- Decision mapping:
|
|
22
|
+
- High or medium confidence with children available: continue.
|
|
23
|
+
- High confidence with terminal node: no_children.
|
|
24
|
+
- Low confidence match: stop.
|
|
25
|
+
- No semantic alignment: no_match.
|
|
26
|
+
- Depth limit reached: max_depth.
|
|
27
|
+
- Provide a concise rationale in one sentence.
|
|
28
|
+
- Keep rationale evidence-based and avoid restating taxonomy labels.
|
|
29
|
+
- Avoid verbosity, speculation, stylistic language, narrative explanation, redundancy, or creativity.
|
|
14
30
|
|
|
15
31
|
Current depth: {{ depth }}
|
|
16
32
|
|
|
@@ -80,8 +80,10 @@ from .classification import (
|
|
|
80
80
|
ClassificationResult,
|
|
81
81
|
ClassificationStep,
|
|
82
82
|
ClassificationStopReason,
|
|
83
|
+
Taxonomy,
|
|
83
84
|
TaxonomyNode,
|
|
84
85
|
flatten_taxonomy,
|
|
86
|
+
taxonomy_enum_path,
|
|
85
87
|
)
|
|
86
88
|
from .extraction import (
|
|
87
89
|
AnnotatedDocumentStructure,
|
|
@@ -108,8 +110,10 @@ __all__ = [
|
|
|
108
110
|
"ClassificationResult",
|
|
109
111
|
"ClassificationStep",
|
|
110
112
|
"ClassificationStopReason",
|
|
113
|
+
"Taxonomy",
|
|
111
114
|
"TaxonomyNode",
|
|
112
115
|
"flatten_taxonomy",
|
|
116
|
+
"taxonomy_enum_path",
|
|
113
117
|
"TaskStructure",
|
|
114
118
|
"PlanStructure",
|
|
115
119
|
"create_plan",
|
|
@@ -115,6 +115,48 @@ class TaxonomyNode(StructureBase):
|
|
|
115
115
|
)
|
|
116
116
|
|
|
117
117
|
|
|
118
|
+
class Taxonomy(StructureBase):
|
|
119
|
+
"""Represent a taxonomy with metadata and root nodes.
|
|
120
|
+
|
|
121
|
+
Attributes
|
|
122
|
+
----------
|
|
123
|
+
name : str
|
|
124
|
+
Human-readable taxonomy name.
|
|
125
|
+
description : str | None
|
|
126
|
+
Optional description of the taxonomy.
|
|
127
|
+
nodes : list[TaxonomyNode]
|
|
128
|
+
Root taxonomy nodes.
|
|
129
|
+
|
|
130
|
+
Methods
|
|
131
|
+
-------
|
|
132
|
+
flattened_nodes
|
|
133
|
+
Return a flattened list of all taxonomy nodes.
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
name: str = spec_field("name", description="Human-readable taxonomy name.")
|
|
137
|
+
description: str | None = spec_field(
|
|
138
|
+
"description",
|
|
139
|
+
description="Optional description of the taxonomy.",
|
|
140
|
+
default=None,
|
|
141
|
+
)
|
|
142
|
+
nodes: list[TaxonomyNode] = spec_field(
|
|
143
|
+
"nodes",
|
|
144
|
+
description="Root taxonomy nodes.",
|
|
145
|
+
default_factory=list,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def flattened_nodes(self) -> list[TaxonomyNode]:
|
|
150
|
+
"""Return a flattened list of all taxonomy nodes.
|
|
151
|
+
|
|
152
|
+
Returns
|
|
153
|
+
-------
|
|
154
|
+
list[TaxonomyNode]
|
|
155
|
+
Depth-first list of taxonomy nodes.
|
|
156
|
+
"""
|
|
157
|
+
return flatten_taxonomy(self.nodes)
|
|
158
|
+
|
|
159
|
+
|
|
118
160
|
def _split_path_identifier(path: str) -> list[str]:
|
|
119
161
|
"""Split a path identifier into label segments.
|
|
120
162
|
|
|
@@ -444,10 +486,42 @@ def flatten_taxonomy(nodes: Iterable[TaxonomyNode]) -> list[TaxonomyNode]:
|
|
|
444
486
|
return flattened
|
|
445
487
|
|
|
446
488
|
|
|
489
|
+
def taxonomy_enum_path(value: Enum | str | None) -> list[str]:
|
|
490
|
+
"""Return the taxonomy path segments for an enum value.
|
|
491
|
+
|
|
492
|
+
Parameters
|
|
493
|
+
----------
|
|
494
|
+
value : Enum or str or None
|
|
495
|
+
Enum member or path identifier string to split. If None, return an
|
|
496
|
+
empty list.
|
|
497
|
+
|
|
498
|
+
Returns
|
|
499
|
+
-------
|
|
500
|
+
list[str]
|
|
501
|
+
Path segments extracted from the taxonomy identifier.
|
|
502
|
+
|
|
503
|
+
Examples
|
|
504
|
+
--------
|
|
505
|
+
>>> StepEnum = Enum("StepEnum", {"ROOT_LEAF": "Root > Leaf"})
|
|
506
|
+
>>> taxonomy_enum_path(StepEnum.ROOT_LEAF)
|
|
507
|
+
['Root', 'Leaf']
|
|
508
|
+
"""
|
|
509
|
+
if value is None:
|
|
510
|
+
return []
|
|
511
|
+
normalized_value = _normalize_enum_value(value)
|
|
512
|
+
if not normalized_value:
|
|
513
|
+
return []
|
|
514
|
+
if not isinstance(normalized_value, str):
|
|
515
|
+
normalized_value = str(normalized_value)
|
|
516
|
+
return _split_path_identifier(normalized_value)
|
|
517
|
+
|
|
518
|
+
|
|
447
519
|
__all__ = [
|
|
448
520
|
"ClassificationResult",
|
|
449
521
|
"ClassificationStep",
|
|
450
522
|
"ClassificationStopReason",
|
|
523
|
+
"Taxonomy",
|
|
451
524
|
"TaxonomyNode",
|
|
452
525
|
"flatten_taxonomy",
|
|
526
|
+
"taxonomy_enum_path",
|
|
453
527
|
]
|
|
@@ -2,20 +2,21 @@ openai_sdk_helpers/__init__.py,sha256=8I469KuzrbAjhNX2A5UnYt_kSmjXqQbfHectTeUx7T
|
|
|
2
2
|
openai_sdk_helpers/cli.py,sha256=BDc08NqWVfL4GBekxMfN5IPPB4pmN1Od9sVpKtIJRZk,8025
|
|
3
3
|
openai_sdk_helpers/environment.py,sha256=mNoswzIdv37tTRhFwA2B6_Onxsm7vhfjPArfwhYuL7g,1825
|
|
4
4
|
openai_sdk_helpers/errors.py,sha256=ZclLp94o08fSsFNjFn_yrX9yTjw1RE0v7A5T1hBChUc,2925
|
|
5
|
-
openai_sdk_helpers/files_api.py,sha256=
|
|
5
|
+
openai_sdk_helpers/files_api.py,sha256=kn-A2pwiNkxMd035PkWDLi_EWzccuEnGyMpLQcY-aVY,14086
|
|
6
6
|
openai_sdk_helpers/logging.py,sha256=djtMo_R_88JjxJeUGU_hSlYCTRv3ffoSu1ocOKrUBIw,1153
|
|
7
7
|
openai_sdk_helpers/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
openai_sdk_helpers/settings.py,sha256=9qTdEIWuvQfQEQI8MU6STUDvbOk-I9FdmAEDjb2Zwx8,13316
|
|
9
9
|
openai_sdk_helpers/tools.py,sha256=8hhcytpmDfoXV16UQbDmDVV0rhLOn8c_VjXO8XaTFLQ,19000
|
|
10
10
|
openai_sdk_helpers/types.py,sha256=ejCG0rYqJhjOQvKLoNnzq-TzcKCFt69GVfi7y805NkU,1451
|
|
11
|
-
openai_sdk_helpers/agent/__init__.py,sha256=
|
|
12
|
-
openai_sdk_helpers/agent/base.py,sha256=
|
|
13
|
-
openai_sdk_helpers/agent/classifier.py,sha256=
|
|
14
|
-
openai_sdk_helpers/agent/configuration.py,sha256=
|
|
11
|
+
openai_sdk_helpers/agent/__init__.py,sha256=qyzKzPhD8KsEl6d79XERK32AK5It_BZNOqChOpBdmhg,1199
|
|
12
|
+
openai_sdk_helpers/agent/base.py,sha256=vLs0oALhxsd_Xy5dGjSZTUFTug-YwZkF1LabQ2ruLxk,29508
|
|
13
|
+
openai_sdk_helpers/agent/classifier.py,sha256=GWgjQxkh1QbZhKlcDCkj-aNgpa8seJWxTfbtXyQSkSg,34889
|
|
14
|
+
openai_sdk_helpers/agent/configuration.py,sha256=ZeH4ErgVe-BZamjUeNONbQi60ViolgYAWh-c8hNAQTw,15810
|
|
15
15
|
openai_sdk_helpers/agent/coordinator.py,sha256=lVjA0yI-GhGKlqbNR_k9GOCrUjFoZ0QoqRaafHckyME,18052
|
|
16
|
-
openai_sdk_helpers/agent/
|
|
16
|
+
openai_sdk_helpers/agent/files.py,sha256=H7UfSZSjFUbv1cjRvNld9kZwIjc5wPq4vynqU8HgGJE,4478
|
|
17
|
+
openai_sdk_helpers/agent/runner.py,sha256=uNf8FiLIlZsbSvE-CopYhv5sPAyxU2te0OaBBxO9RWY,3613
|
|
17
18
|
openai_sdk_helpers/agent/summarizer.py,sha256=-yVm-KdTvGRXGj1MlEikTAFYVlPoovLNIL3Tc_WYIzs,3653
|
|
18
|
-
openai_sdk_helpers/agent/translator.py,sha256=
|
|
19
|
+
openai_sdk_helpers/agent/translator.py,sha256=Skke5wyZTpo_9gMcwHRyoBQl00zTBeXnIUujUIr2ZDE,6017
|
|
19
20
|
openai_sdk_helpers/agent/utils.py,sha256=DTD5foCqGYfXf13F2bZMYIQROl7SbDSy5GDPGi0Zl-0,1089
|
|
20
21
|
openai_sdk_helpers/agent/validator.py,sha256=krktzjaHhEprn76F7hD4cH6H2CwucmFN1KWJ_vjl01g,4774
|
|
21
22
|
openai_sdk_helpers/agent/search/__init__.py,sha256=LXXzEcX2MU7_htHRdRCGPw0hsr9CrZn0ESii7GZJMBw,806
|
|
@@ -29,7 +30,7 @@ openai_sdk_helpers/extract/extractor.py,sha256=vmRJyhKDEYAVfRk0KMgLH5hTqUfDAUyWB
|
|
|
29
30
|
openai_sdk_helpers/extract/generator.py,sha256=K9Euq0IaWs82oe5aRm73_18DelLKYyuH8VhfZ1_ZCEU,14695
|
|
30
31
|
openai_sdk_helpers/prompt/__init__.py,sha256=MOqgKwG9KLqKudoKRlUfLxiSmdOi2aD6hNrWDFqLHkk,418
|
|
31
32
|
openai_sdk_helpers/prompt/base.py,sha256=6X0zeopEvO0ba8207O8Nnj1QvFZEZier7kNNh4qkcmE,7782
|
|
32
|
-
openai_sdk_helpers/prompt/classifier.jinja,sha256=
|
|
33
|
+
openai_sdk_helpers/prompt/classifier.jinja,sha256=6od2DyyEUUrT0AmeJfJ57gJxJ6gdbPc11vff-VNywNk,1895
|
|
33
34
|
openai_sdk_helpers/prompt/extractor_config_agent_instructions.jinja,sha256=vCrsoUnsgHWSr7OS_ojMUjmPtHfbyv9bzKfaMaCJ99E,329
|
|
34
35
|
openai_sdk_helpers/prompt/extractor_config_generator.jinja,sha256=9rZ1PZdoQtnxDxFUlKRb0SooIEfNw4_Em99n9xvFyyU,960
|
|
35
36
|
openai_sdk_helpers/prompt/extractor_config_generator_instructions.jinja,sha256=GqV3DrGObyER_Fa-GMGGqhWBrQIH9FFlyKdgTjidyzg,534
|
|
@@ -54,10 +55,10 @@ openai_sdk_helpers/response/vector_store.py,sha256=HClp6O_g20uklQTY7trC4age3rtDm
|
|
|
54
55
|
openai_sdk_helpers/streamlit_app/__init__.py,sha256=3yAkl6qV71cqtT5YFZuC9Bkqit0NtffDV6jmMWpT1k4,812
|
|
55
56
|
openai_sdk_helpers/streamlit_app/app.py,sha256=kkjtdCKVwrJ9nZWuBArm3dhvcjMESX0TMqAiF61_JLM,17402
|
|
56
57
|
openai_sdk_helpers/streamlit_app/configuration.py,sha256=0KeJ4HqCNFthBHsedV6ptqHluAcTPBb5_TujFOGkIUU,16685
|
|
57
|
-
openai_sdk_helpers/structure/__init__.py,sha256
|
|
58
|
+
openai_sdk_helpers/structure/__init__.py,sha256=ErtNlTADV4cc7s27i_CbQATd2PD9xcJd8_D273QmyOI,4253
|
|
58
59
|
openai_sdk_helpers/structure/agent_blueprint.py,sha256=VyJWkgPNzAYKRDMeR1M4kE6qqQURnwqtrrEn0TRJf0g,9698
|
|
59
60
|
openai_sdk_helpers/structure/base.py,sha256=UrnNNU9qQ9mEES8MB9y6QESbDgPXH47XW8LVWSxYUYM,25280
|
|
60
|
-
openai_sdk_helpers/structure/classification.py,sha256=
|
|
61
|
+
openai_sdk_helpers/structure/classification.py,sha256=Vk7LGG3pty5T0Eh94Pao7kWY8_Wcuw37ywdPDcnElrg,15316
|
|
61
62
|
openai_sdk_helpers/structure/extraction.py,sha256=wODP0iLAhhsdQkMWRYPYTiLUMU8bFMKiBjPl3PKUleg,37335
|
|
62
63
|
openai_sdk_helpers/structure/prompt.py,sha256=ZfsaHdA0hj5zmZDrOdpXjCsC8U-jjzwFG4JBsWYiaH4,1535
|
|
63
64
|
openai_sdk_helpers/structure/responses.py,sha256=WUwh0DhXj24pkvgqH1FMkdx5V2ArdvdtrDN_fuMBtDU,4882
|
|
@@ -91,8 +92,8 @@ openai_sdk_helpers/vector_storage/__init__.py,sha256=L5LxO09puh9_yBB9IDTvc1CvVkA
|
|
|
91
92
|
openai_sdk_helpers/vector_storage/cleanup.py,sha256=sZ4ZSTlnjF52o9Cc8A9dTX37ZYXXDxS_fdIpoOBWvrg,3666
|
|
92
93
|
openai_sdk_helpers/vector_storage/storage.py,sha256=t_ukacaXRa9EXE4-3BxsrB4Rjhu6nTu7NA9IjCJBIpQ,24259
|
|
93
94
|
openai_sdk_helpers/vector_storage/types.py,sha256=jTCcOYMeOpZWvcse0z4T3MVs-RBOPC-fqWTBeQrgafU,1639
|
|
94
|
-
openai_sdk_helpers-0.6.
|
|
95
|
-
openai_sdk_helpers-0.6.
|
|
96
|
-
openai_sdk_helpers-0.6.
|
|
97
|
-
openai_sdk_helpers-0.6.
|
|
98
|
-
openai_sdk_helpers-0.6.
|
|
95
|
+
openai_sdk_helpers-0.6.2.dist-info/METADATA,sha256=TGZjA_nQ2FDm9KeEjun4MrIAUl-zAxB-Xi5UCUkn8nY,24622
|
|
96
|
+
openai_sdk_helpers-0.6.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
97
|
+
openai_sdk_helpers-0.6.2.dist-info/entry_points.txt,sha256=gEOD1ZeXe8d2OP-KzUlG-b_9D9yUZTCt-GFW3EDbIIY,63
|
|
98
|
+
openai_sdk_helpers-0.6.2.dist-info/licenses/LICENSE,sha256=CUhc1NrE50bs45tcXF7OcTQBKEvkUuLqeOHgrWQ5jaA,1067
|
|
99
|
+
openai_sdk_helpers-0.6.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|