openai-sdk-helpers 0.1.0__py3-none-any.whl → 0.1.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/__init__.py +44 -7
- openai_sdk_helpers/agent/base.py +5 -1
- openai_sdk_helpers/agent/coordination.py +4 -5
- openai_sdk_helpers/agent/runner.py +4 -1
- openai_sdk_helpers/agent/search/base.py +1 -0
- openai_sdk_helpers/agent/search/vector.py +2 -0
- openai_sdk_helpers/cli.py +265 -0
- openai_sdk_helpers/config.py +93 -2
- openai_sdk_helpers/context_manager.py +1 -1
- openai_sdk_helpers/deprecation.py +167 -0
- openai_sdk_helpers/environment.py +3 -2
- openai_sdk_helpers/errors.py +0 -12
- openai_sdk_helpers/files_api.py +373 -0
- openai_sdk_helpers/logging_config.py +24 -95
- openai_sdk_helpers/prompt/base.py +1 -1
- openai_sdk_helpers/response/__init__.py +7 -3
- openai_sdk_helpers/response/base.py +217 -147
- openai_sdk_helpers/response/config.py +16 -1
- openai_sdk_helpers/response/files.py +392 -0
- openai_sdk_helpers/response/messages.py +1 -0
- openai_sdk_helpers/retry.py +1 -1
- openai_sdk_helpers/streamlit_app/app.py +97 -7
- openai_sdk_helpers/streamlit_app/streamlit_web_search.py +15 -8
- openai_sdk_helpers/structure/base.py +6 -6
- openai_sdk_helpers/structure/plan/helpers.py +1 -0
- openai_sdk_helpers/structure/plan/task.py +7 -7
- openai_sdk_helpers/tools.py +116 -13
- openai_sdk_helpers/utils/__init__.py +100 -35
- openai_sdk_helpers/{async_utils.py → utils/async_utils.py} +5 -6
- openai_sdk_helpers/utils/coercion.py +138 -0
- openai_sdk_helpers/utils/deprecation.py +167 -0
- openai_sdk_helpers/utils/encoding.py +189 -0
- openai_sdk_helpers/utils/json_utils.py +98 -0
- openai_sdk_helpers/utils/output_validation.py +448 -0
- openai_sdk_helpers/utils/path_utils.py +46 -0
- openai_sdk_helpers/{validation.py → utils/validation.py} +7 -3
- openai_sdk_helpers/vector_storage/storage.py +59 -28
- {openai_sdk_helpers-0.1.0.dist-info → openai_sdk_helpers-0.1.2.dist-info}/METADATA +152 -3
- openai_sdk_helpers-0.1.2.dist-info/RECORD +79 -0
- openai_sdk_helpers-0.1.2.dist-info/entry_points.txt +2 -0
- openai_sdk_helpers/utils/core.py +0 -596
- openai_sdk_helpers-0.1.0.dist-info/RECORD +0 -69
- {openai_sdk_helpers-0.1.0.dist-info → openai_sdk_helpers-0.1.2.dist-info}/WHEEL +0 -0
- {openai_sdk_helpers-0.1.0.dist-info → openai_sdk_helpers-0.1.2.dist-info}/licenses/LICENSE +0 -0
openai_sdk_helpers/__init__.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from .async_utils import run_coroutine_thread_safe, run_coroutine_with_fallback
|
|
5
|
+
from .utils.async_utils import run_coroutine_thread_safe, run_coroutine_with_fallback
|
|
6
6
|
from .context_manager import (
|
|
7
7
|
AsyncManagedResource,
|
|
8
8
|
ManagedResource,
|
|
@@ -22,9 +22,8 @@ from .errors import (
|
|
|
22
22
|
AsyncExecutionError,
|
|
23
23
|
ResourceCleanupError,
|
|
24
24
|
)
|
|
25
|
-
from .logging_config import LoggerFactory
|
|
26
25
|
from .retry import with_exponential_backoff
|
|
27
|
-
from .validation import (
|
|
26
|
+
from .utils.validation import (
|
|
28
27
|
validate_choice,
|
|
29
28
|
validate_dict_mapping,
|
|
30
29
|
validate_list_items,
|
|
@@ -52,6 +51,7 @@ from .structure import (
|
|
|
52
51
|
)
|
|
53
52
|
from .prompt import PromptRenderer
|
|
54
53
|
from .config import OpenAISettings
|
|
54
|
+
from .files_api import FilesAPIManager, FilePurpose
|
|
55
55
|
from .vector_storage import VectorStorage, VectorStorageFileInfo, VectorStorageFileStats
|
|
56
56
|
from .agent import (
|
|
57
57
|
AgentBase,
|
|
@@ -78,9 +78,28 @@ from .response import (
|
|
|
78
78
|
from .tools import (
|
|
79
79
|
serialize_tool_result,
|
|
80
80
|
tool_handler_factory,
|
|
81
|
+
StructureType,
|
|
82
|
+
ToolSpec,
|
|
83
|
+
build_tool_definitions,
|
|
81
84
|
)
|
|
82
|
-
from .
|
|
83
|
-
|
|
85
|
+
from .config import build_openai_settings
|
|
86
|
+
from .utils.deprecation import (
|
|
87
|
+
deprecated,
|
|
88
|
+
warn_deprecated,
|
|
89
|
+
DeprecationHelper,
|
|
90
|
+
)
|
|
91
|
+
from .utils.output_validation import (
|
|
92
|
+
ValidationResult,
|
|
93
|
+
ValidationRule,
|
|
94
|
+
JSONSchemaValidator,
|
|
95
|
+
SemanticValidator,
|
|
96
|
+
LengthValidator,
|
|
97
|
+
OutputValidator,
|
|
98
|
+
validate_output,
|
|
99
|
+
)
|
|
100
|
+
from .types import (
|
|
101
|
+
SupportsOpenAIClient,
|
|
102
|
+
OpenAIClient,
|
|
84
103
|
)
|
|
85
104
|
|
|
86
105
|
__all__ = [
|
|
@@ -98,8 +117,6 @@ __all__ = [
|
|
|
98
117
|
"InputValidationError",
|
|
99
118
|
"AsyncExecutionError",
|
|
100
119
|
"ResourceCleanupError",
|
|
101
|
-
# Logging
|
|
102
|
-
"LoggerFactory",
|
|
103
120
|
# Retry utilities
|
|
104
121
|
"with_exponential_backoff",
|
|
105
122
|
# Context managers
|
|
@@ -122,6 +139,8 @@ __all__ = [
|
|
|
122
139
|
"spec_field",
|
|
123
140
|
"PromptRenderer",
|
|
124
141
|
"OpenAISettings",
|
|
142
|
+
"FilesAPIManager",
|
|
143
|
+
"FilePurpose",
|
|
125
144
|
"VectorStorage",
|
|
126
145
|
"VectorStorageFileInfo",
|
|
127
146
|
"VectorStorageFileStats",
|
|
@@ -154,8 +173,26 @@ __all__ = [
|
|
|
154
173
|
"attach_vector_store",
|
|
155
174
|
"serialize_tool_result",
|
|
156
175
|
"tool_handler_factory",
|
|
176
|
+
"StructureType",
|
|
177
|
+
"ToolSpec",
|
|
178
|
+
"build_tool_definitions",
|
|
157
179
|
"build_openai_settings",
|
|
158
180
|
"create_plan",
|
|
159
181
|
"execute_task",
|
|
160
182
|
"execute_plan",
|
|
183
|
+
# Type definitions
|
|
184
|
+
"SupportsOpenAIClient",
|
|
185
|
+
"OpenAIClient",
|
|
186
|
+
# Deprecation utilities
|
|
187
|
+
"deprecated",
|
|
188
|
+
"warn_deprecated",
|
|
189
|
+
"DeprecationHelper",
|
|
190
|
+
# Output validation
|
|
191
|
+
"ValidationResult",
|
|
192
|
+
"ValidationRule",
|
|
193
|
+
"JSONSchemaValidator",
|
|
194
|
+
"SemanticValidator",
|
|
195
|
+
"LengthValidator",
|
|
196
|
+
"OutputValidator",
|
|
197
|
+
"validate_output",
|
|
161
198
|
]
|
openai_sdk_helpers/agent/base.py
CHANGED
|
@@ -146,6 +146,7 @@ class AgentBase:
|
|
|
146
146
|
def from_config(
|
|
147
147
|
cls,
|
|
148
148
|
config: AgentConfigLike,
|
|
149
|
+
*,
|
|
149
150
|
run_context_wrapper: Optional[RunContextWrapper[Dict[str, Any]]] = None,
|
|
150
151
|
prompt_dir: Optional[Path] = None,
|
|
151
152
|
default_model: Optional[str] = None,
|
|
@@ -213,7 +214,7 @@ class AgentBase:
|
|
|
213
214
|
return self._template.render(context)
|
|
214
215
|
|
|
215
216
|
def get_prompt(
|
|
216
|
-
self, run_context_wrapper: RunContextWrapper[Dict[str, Any]], _: Agent
|
|
217
|
+
self, run_context_wrapper: RunContextWrapper[Dict[str, Any]], *, _: Agent
|
|
217
218
|
) -> str:
|
|
218
219
|
"""Render the agent prompt using the provided run context.
|
|
219
220
|
|
|
@@ -257,6 +258,7 @@ class AgentBase:
|
|
|
257
258
|
async def run_async(
|
|
258
259
|
self,
|
|
259
260
|
input: str,
|
|
261
|
+
*,
|
|
260
262
|
context: Optional[Dict[str, Any]] = None,
|
|
261
263
|
output_type: Optional[Any] = None,
|
|
262
264
|
) -> Any:
|
|
@@ -288,6 +290,7 @@ class AgentBase:
|
|
|
288
290
|
def run_sync(
|
|
289
291
|
self,
|
|
290
292
|
input: str,
|
|
293
|
+
*,
|
|
291
294
|
context: Optional[Dict[str, Any]] = None,
|
|
292
295
|
output_type: Optional[Any] = None,
|
|
293
296
|
) -> Any:
|
|
@@ -317,6 +320,7 @@ class AgentBase:
|
|
|
317
320
|
def run_streamed(
|
|
318
321
|
self,
|
|
319
322
|
input: str,
|
|
323
|
+
*,
|
|
320
324
|
context: Optional[Dict[str, Any]] = None,
|
|
321
325
|
output_type: Optional[Any] = None,
|
|
322
326
|
) -> RunResultStreaming:
|
|
@@ -12,8 +12,7 @@ from typing import Any, Callable, Dict, List, Optional
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
from ..structure import TaskStructure, PlanStructure, PromptStructure
|
|
15
|
-
from ..
|
|
16
|
-
from ..utils import JSONSerializable, log
|
|
15
|
+
from ..utils import JSONSerializable, ensure_directory, log
|
|
17
16
|
from .base import AgentBase
|
|
18
17
|
from .config import AgentConfig
|
|
19
18
|
from ..structure.plan.enum import AgentEnum
|
|
@@ -49,6 +48,7 @@ class CoordinatorAgent(AgentBase, JSONSerializable):
|
|
|
49
48
|
|
|
50
49
|
def __init__(
|
|
51
50
|
self,
|
|
51
|
+
*,
|
|
52
52
|
prompt_fn: PromptFn,
|
|
53
53
|
build_plan_fn: BuildPlanFn,
|
|
54
54
|
execute_plan_fn: ExecutePlanFn,
|
|
@@ -207,7 +207,7 @@ class CoordinatorAgent(AgentBase, JSONSerializable):
|
|
|
207
207
|
"""
|
|
208
208
|
if not self.start_date:
|
|
209
209
|
self.start_date = datetime.now(timezone.utc)
|
|
210
|
-
start_date_str = self.start_date.strftime(
|
|
210
|
+
start_date_str = self.start_date.strftime("%Y%m%d_%H%M%S")
|
|
211
211
|
return self._module_data_path / self._name / f"{start_date_str}.json"
|
|
212
212
|
|
|
213
213
|
def save(self) -> Path:
|
|
@@ -447,8 +447,7 @@ class CoordinatorAgent(AgentBase, JSONSerializable):
|
|
|
447
447
|
/ "coordinator_agent"
|
|
448
448
|
/ timestamp
|
|
449
449
|
)
|
|
450
|
-
self._run_directory
|
|
451
|
-
return self._run_directory
|
|
450
|
+
return ensure_directory(self._run_directory)
|
|
452
451
|
|
|
453
452
|
@staticmethod
|
|
454
453
|
def _task_label(task: TaskStructure) -> str:
|
|
@@ -11,12 +11,13 @@ from typing import Any, Dict, Optional
|
|
|
11
11
|
|
|
12
12
|
from agents import Agent, RunResult, RunResultStreaming, Runner
|
|
13
13
|
|
|
14
|
-
from openai_sdk_helpers.async_utils import run_coroutine_with_fallback
|
|
14
|
+
from openai_sdk_helpers.utils.async_utils import run_coroutine_with_fallback
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
async def run_async(
|
|
18
18
|
agent: Agent,
|
|
19
19
|
input: str,
|
|
20
|
+
*,
|
|
20
21
|
context: Optional[Dict[str, Any]] = None,
|
|
21
22
|
output_type: Optional[Any] = None,
|
|
22
23
|
) -> Any:
|
|
@@ -57,6 +58,7 @@ async def run_async(
|
|
|
57
58
|
def run_sync(
|
|
58
59
|
agent: Agent,
|
|
59
60
|
input: str,
|
|
61
|
+
*,
|
|
60
62
|
context: Optional[Dict[str, Any]] = None,
|
|
61
63
|
output_type: Optional[Any] = None,
|
|
62
64
|
) -> Any:
|
|
@@ -103,6 +105,7 @@ def run_sync(
|
|
|
103
105
|
def run_streamed(
|
|
104
106
|
agent: Agent,
|
|
105
107
|
input: str,
|
|
108
|
+
*,
|
|
106
109
|
context: Optional[Dict[str, Any]] = None,
|
|
107
110
|
output_type: Optional[Any] = None,
|
|
108
111
|
) -> RunResultStreaming:
|
|
@@ -80,6 +80,7 @@ class VectorSearchTool(
|
|
|
80
80
|
|
|
81
81
|
def __init__(
|
|
82
82
|
self,
|
|
83
|
+
*,
|
|
83
84
|
prompt_dir: Optional[Path] = None,
|
|
84
85
|
default_model: Optional[str] = None,
|
|
85
86
|
store_name: Optional[str] = None,
|
|
@@ -256,6 +257,7 @@ class VectorSearch:
|
|
|
256
257
|
|
|
257
258
|
def __init__(
|
|
258
259
|
self,
|
|
260
|
+
*,
|
|
259
261
|
prompt_dir: Optional[Path] = None,
|
|
260
262
|
default_model: Optional[str] = None,
|
|
261
263
|
vector_store_name: Optional[str] = None,
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"""Command-line interface for openai-sdk-helpers development.
|
|
2
|
+
|
|
3
|
+
Provides CLI commands for testing agents, validating templates,
|
|
4
|
+
and inspecting the response registry.
|
|
5
|
+
|
|
6
|
+
Commands
|
|
7
|
+
--------
|
|
8
|
+
agent test
|
|
9
|
+
Test an agent locally with sample inputs.
|
|
10
|
+
template validate
|
|
11
|
+
Validate Jinja2 templates for syntax errors.
|
|
12
|
+
registry list
|
|
13
|
+
List all registered response configurations.
|
|
14
|
+
registry inspect
|
|
15
|
+
Inspect a specific configuration.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import argparse
|
|
21
|
+
import json
|
|
22
|
+
import sys
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Any
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
import openai_sdk_helpers
|
|
28
|
+
|
|
29
|
+
__version__ = getattr(openai_sdk_helpers, "__version__", "unknown")
|
|
30
|
+
except ImportError:
|
|
31
|
+
__version__ = "unknown"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def cmd_agent_test(args: argparse.Namespace) -> int:
|
|
35
|
+
"""Test an agent locally.
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
args : argparse.Namespace
|
|
40
|
+
Command arguments containing agent_name and input.
|
|
41
|
+
|
|
42
|
+
Returns
|
|
43
|
+
-------
|
|
44
|
+
int
|
|
45
|
+
Exit code (0 for success).
|
|
46
|
+
"""
|
|
47
|
+
print(f"Testing agent: {args.agent_name}")
|
|
48
|
+
print(f"Input: {args.input}")
|
|
49
|
+
print("\n[Not yet implemented - agent testing framework coming soon]")
|
|
50
|
+
return 0
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def cmd_template_validate(args: argparse.Namespace) -> int:
|
|
54
|
+
"""Validate Jinja2 templates.
|
|
55
|
+
|
|
56
|
+
Parameters
|
|
57
|
+
----------
|
|
58
|
+
args : argparse.Namespace
|
|
59
|
+
Command arguments containing template_path.
|
|
60
|
+
|
|
61
|
+
Returns
|
|
62
|
+
-------
|
|
63
|
+
int
|
|
64
|
+
Exit code (0 for success, 1 for validation errors).
|
|
65
|
+
"""
|
|
66
|
+
from jinja2 import Environment, FileSystemLoader, TemplateSyntaxError
|
|
67
|
+
|
|
68
|
+
template_path = Path(args.template_path)
|
|
69
|
+
|
|
70
|
+
if not template_path.exists():
|
|
71
|
+
print(f"Error: Path not found: {template_path}", file=sys.stderr)
|
|
72
|
+
return 1
|
|
73
|
+
|
|
74
|
+
if template_path.is_file():
|
|
75
|
+
# Validate single file
|
|
76
|
+
templates = [template_path]
|
|
77
|
+
base_dir = template_path.parent
|
|
78
|
+
else:
|
|
79
|
+
# Validate directory
|
|
80
|
+
templates = list(template_path.glob("**/*.jinja"))
|
|
81
|
+
base_dir = template_path
|
|
82
|
+
|
|
83
|
+
if not templates:
|
|
84
|
+
print(f"No .jinja templates found in {template_path}")
|
|
85
|
+
return 0
|
|
86
|
+
|
|
87
|
+
env = Environment(loader=FileSystemLoader(base_dir))
|
|
88
|
+
errors = []
|
|
89
|
+
|
|
90
|
+
for template_file in templates:
|
|
91
|
+
relative_path = template_file.relative_to(base_dir)
|
|
92
|
+
try:
|
|
93
|
+
env.get_template(str(relative_path))
|
|
94
|
+
print(f"✓ {relative_path}")
|
|
95
|
+
except TemplateSyntaxError as e:
|
|
96
|
+
errors.append((relative_path, str(e)))
|
|
97
|
+
print(f"✗ {relative_path}: {e}", file=sys.stderr)
|
|
98
|
+
|
|
99
|
+
if errors:
|
|
100
|
+
print(f"\n{len(errors)} template(s) with errors", file=sys.stderr)
|
|
101
|
+
return 1
|
|
102
|
+
|
|
103
|
+
print(f"\n{len(templates)} template(s) validated successfully")
|
|
104
|
+
return 0
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def cmd_registry_list(args: argparse.Namespace) -> int:
|
|
108
|
+
"""List all registered response configurations.
|
|
109
|
+
|
|
110
|
+
Parameters
|
|
111
|
+
----------
|
|
112
|
+
args : argparse.Namespace
|
|
113
|
+
Command arguments.
|
|
114
|
+
|
|
115
|
+
Returns
|
|
116
|
+
-------
|
|
117
|
+
int
|
|
118
|
+
Exit code (0 for success).
|
|
119
|
+
"""
|
|
120
|
+
try:
|
|
121
|
+
from openai_sdk_helpers import get_default_registry
|
|
122
|
+
except ImportError:
|
|
123
|
+
print("Error: openai_sdk_helpers not installed", file=sys.stderr)
|
|
124
|
+
return 1
|
|
125
|
+
|
|
126
|
+
registry = get_default_registry()
|
|
127
|
+
names = registry.list_names()
|
|
128
|
+
|
|
129
|
+
if not names:
|
|
130
|
+
print("No configurations registered")
|
|
131
|
+
return 0
|
|
132
|
+
|
|
133
|
+
print("Registered configurations:")
|
|
134
|
+
for name in sorted(names):
|
|
135
|
+
config = registry.get(name)
|
|
136
|
+
tools_count = len(config.tools) if config.tools else 0
|
|
137
|
+
print(f" - {name} ({tools_count} tools)")
|
|
138
|
+
|
|
139
|
+
return 0
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def cmd_registry_inspect(args: argparse.Namespace) -> int:
|
|
143
|
+
"""Inspect a specific configuration.
|
|
144
|
+
|
|
145
|
+
Parameters
|
|
146
|
+
----------
|
|
147
|
+
args : argparse.Namespace
|
|
148
|
+
Command arguments containing config_name.
|
|
149
|
+
|
|
150
|
+
Returns
|
|
151
|
+
-------
|
|
152
|
+
int
|
|
153
|
+
Exit code (0 for success, 1 for not found).
|
|
154
|
+
"""
|
|
155
|
+
try:
|
|
156
|
+
from openai_sdk_helpers import get_default_registry
|
|
157
|
+
except ImportError:
|
|
158
|
+
print("Error: openai_sdk_helpers not installed", file=sys.stderr)
|
|
159
|
+
return 1
|
|
160
|
+
|
|
161
|
+
registry = get_default_registry()
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
config = registry.get(args.config_name)
|
|
165
|
+
except KeyError:
|
|
166
|
+
print(f"Error: Configuration '{args.config_name}' not found", file=sys.stderr)
|
|
167
|
+
print("\nAvailable configurations:")
|
|
168
|
+
for name in sorted(registry.list_names()):
|
|
169
|
+
print(f" - {name}")
|
|
170
|
+
return 1
|
|
171
|
+
|
|
172
|
+
print(f"Configuration: {config.name}")
|
|
173
|
+
instructions_str = str(config.instructions)
|
|
174
|
+
instructions_preview = (
|
|
175
|
+
instructions_str[:100] if len(instructions_str) > 100 else instructions_str
|
|
176
|
+
)
|
|
177
|
+
print(f"Instructions: {instructions_preview}...")
|
|
178
|
+
print(f"Tools: {len(config.tools) if config.tools else 0}")
|
|
179
|
+
|
|
180
|
+
if config.tools:
|
|
181
|
+
print("\nTool names:")
|
|
182
|
+
for tool in config.tools:
|
|
183
|
+
tool_name = tool.get("function", {}).get("name", "unknown")
|
|
184
|
+
print(f" - {tool_name}")
|
|
185
|
+
|
|
186
|
+
return 0
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def main(argv: list[str] | None = None) -> int:
|
|
190
|
+
"""Run the CLI interface.
|
|
191
|
+
|
|
192
|
+
Parameters
|
|
193
|
+
----------
|
|
194
|
+
argv : list[str], optional
|
|
195
|
+
Command-line arguments. If None, uses sys.argv.
|
|
196
|
+
|
|
197
|
+
Returns
|
|
198
|
+
-------
|
|
199
|
+
int
|
|
200
|
+
Exit code.
|
|
201
|
+
"""
|
|
202
|
+
parser = argparse.ArgumentParser(
|
|
203
|
+
prog="openai-helpers",
|
|
204
|
+
description="OpenAI SDK Helpers CLI",
|
|
205
|
+
)
|
|
206
|
+
parser.add_argument(
|
|
207
|
+
"--version",
|
|
208
|
+
action="version",
|
|
209
|
+
version=f"openai-sdk-helpers {__version__}",
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
subparsers = parser.add_subparsers(dest="command", help="Commands")
|
|
213
|
+
|
|
214
|
+
# Agent test command
|
|
215
|
+
agent_parser = subparsers.add_parser("agent", help="Agent operations")
|
|
216
|
+
agent_sub = agent_parser.add_subparsers(dest="agent_command")
|
|
217
|
+
|
|
218
|
+
test_parser = agent_sub.add_parser("test", help="Test an agent")
|
|
219
|
+
test_parser.add_argument("agent_name", help="Agent name to test")
|
|
220
|
+
test_parser.add_argument("--input", default="", help="Test input")
|
|
221
|
+
|
|
222
|
+
# Template validate command
|
|
223
|
+
template_parser = subparsers.add_parser("template", help="Template operations")
|
|
224
|
+
template_sub = template_parser.add_subparsers(dest="template_command")
|
|
225
|
+
|
|
226
|
+
validate_parser = template_sub.add_parser("validate", help="Validate templates")
|
|
227
|
+
validate_parser.add_argument(
|
|
228
|
+
"template_path",
|
|
229
|
+
help="Path to template file or directory",
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
# Registry commands
|
|
233
|
+
registry_parser = subparsers.add_parser("registry", help="Registry operations")
|
|
234
|
+
registry_sub = registry_parser.add_subparsers(dest="registry_command")
|
|
235
|
+
|
|
236
|
+
registry_sub.add_parser("list", help="List registered configurations")
|
|
237
|
+
|
|
238
|
+
inspect_parser = registry_sub.add_parser("inspect", help="Inspect configuration")
|
|
239
|
+
inspect_parser.add_argument("config_name", help="Configuration name")
|
|
240
|
+
|
|
241
|
+
args = parser.parse_args(argv)
|
|
242
|
+
|
|
243
|
+
if not args.command:
|
|
244
|
+
parser.print_help()
|
|
245
|
+
return 0
|
|
246
|
+
|
|
247
|
+
# Route commands
|
|
248
|
+
if args.command == "agent":
|
|
249
|
+
if args.agent_command == "test":
|
|
250
|
+
return cmd_agent_test(args)
|
|
251
|
+
elif args.command == "template":
|
|
252
|
+
if args.template_command == "validate":
|
|
253
|
+
return cmd_template_validate(args)
|
|
254
|
+
elif args.command == "registry":
|
|
255
|
+
if args.registry_command == "list":
|
|
256
|
+
return cmd_registry_list(args)
|
|
257
|
+
elif args.registry_command == "inspect":
|
|
258
|
+
return cmd_registry_inspect(args)
|
|
259
|
+
|
|
260
|
+
parser.print_help()
|
|
261
|
+
return 0
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
if __name__ == "__main__":
|
|
265
|
+
sys.exit(main())
|
openai_sdk_helpers/config.py
CHANGED
|
@@ -116,7 +116,7 @@ class OpenAISettings(BaseModel):
|
|
|
116
116
|
|
|
117
117
|
@classmethod
|
|
118
118
|
def from_env(
|
|
119
|
-
cls, dotenv_path: Path | None = None, **overrides: Any
|
|
119
|
+
cls, *, dotenv_path: Path | None = None, **overrides: Any
|
|
120
120
|
) -> OpenAISettings:
|
|
121
121
|
"""Load settings from the environment and optional overrides.
|
|
122
122
|
|
|
@@ -232,4 +232,95 @@ class OpenAISettings(BaseModel):
|
|
|
232
232
|
return OpenAI(**self.client_kwargs())
|
|
233
233
|
|
|
234
234
|
|
|
235
|
-
__all__ = ["OpenAISettings"]
|
|
235
|
+
__all__ = ["OpenAISettings", "build_openai_settings"]
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def build_openai_settings(
|
|
239
|
+
api_key: str | None = None,
|
|
240
|
+
org_id: str | None = None,
|
|
241
|
+
project_id: str | None = None,
|
|
242
|
+
base_url: str | None = None,
|
|
243
|
+
default_model: str | None = None,
|
|
244
|
+
timeout: float | str | None = None,
|
|
245
|
+
max_retries: int | str | None = None,
|
|
246
|
+
dotenv_path: Path | None = None,
|
|
247
|
+
**extra_kwargs: Any,
|
|
248
|
+
) -> OpenAISettings:
|
|
249
|
+
"""Build OpenAISettings with validation and clear errors.
|
|
250
|
+
|
|
251
|
+
Parameters
|
|
252
|
+
----------
|
|
253
|
+
api_key : str or None, default None
|
|
254
|
+
API key for OpenAI authentication. If None, reads from OPENAI_API_KEY.
|
|
255
|
+
org_id : str or None, default None
|
|
256
|
+
Organization ID. If None, reads from OPENAI_ORG_ID.
|
|
257
|
+
project_id : str or None, default None
|
|
258
|
+
Project ID. If None, reads from OPENAI_PROJECT_ID.
|
|
259
|
+
base_url : str or None, default None
|
|
260
|
+
Base URL for API requests. If None, reads from OPENAI_BASE_URL.
|
|
261
|
+
default_model : str or None, default None
|
|
262
|
+
Default model name. If None, reads from OPENAI_MODEL.
|
|
263
|
+
timeout : float, str, or None, default None
|
|
264
|
+
Request timeout in seconds. If None, reads from OPENAI_TIMEOUT.
|
|
265
|
+
Strings are parsed to float.
|
|
266
|
+
max_retries : int, str, or None, default None
|
|
267
|
+
Maximum retry attempts. If None, reads from OPENAI_MAX_RETRIES.
|
|
268
|
+
Strings are parsed to int.
|
|
269
|
+
dotenv_path : Path or None, default None
|
|
270
|
+
Path to a .env file. If None, uses environment only.
|
|
271
|
+
**extra_kwargs : Any
|
|
272
|
+
Additional keyword arguments forwarded to ``extra_client_kwargs``.
|
|
273
|
+
|
|
274
|
+
Returns
|
|
275
|
+
-------
|
|
276
|
+
OpenAISettings
|
|
277
|
+
Configured settings instance.
|
|
278
|
+
|
|
279
|
+
Raises
|
|
280
|
+
------
|
|
281
|
+
ValueError
|
|
282
|
+
If required values are missing or cannot be parsed.
|
|
283
|
+
TypeError
|
|
284
|
+
If timeout or max_retries have invalid types.
|
|
285
|
+
"""
|
|
286
|
+
parsed_timeout: float | None = None
|
|
287
|
+
if timeout is not None:
|
|
288
|
+
try:
|
|
289
|
+
parsed_timeout = coerce_optional_float(timeout)
|
|
290
|
+
except (ValueError, TypeError) as exc:
|
|
291
|
+
raise ValueError(
|
|
292
|
+
f"Invalid timeout value '{timeout}'. Must be a number or numeric string."
|
|
293
|
+
) from exc
|
|
294
|
+
|
|
295
|
+
parsed_max_retries: int | None = None
|
|
296
|
+
if max_retries is not None:
|
|
297
|
+
try:
|
|
298
|
+
parsed_max_retries = coerce_optional_int(max_retries)
|
|
299
|
+
except (ValueError, TypeError) as exc:
|
|
300
|
+
raise ValueError(
|
|
301
|
+
f"Invalid max_retries value '{max_retries}'. "
|
|
302
|
+
"Must be an integer or numeric string."
|
|
303
|
+
) from exc
|
|
304
|
+
|
|
305
|
+
overrides = {}
|
|
306
|
+
if api_key is not None:
|
|
307
|
+
overrides["api_key"] = api_key
|
|
308
|
+
if org_id is not None:
|
|
309
|
+
overrides["org_id"] = org_id
|
|
310
|
+
if project_id is not None:
|
|
311
|
+
overrides["project_id"] = project_id
|
|
312
|
+
if base_url is not None:
|
|
313
|
+
overrides["base_url"] = base_url
|
|
314
|
+
if default_model is not None:
|
|
315
|
+
overrides["default_model"] = default_model
|
|
316
|
+
if parsed_timeout is not None:
|
|
317
|
+
overrides["timeout"] = parsed_timeout
|
|
318
|
+
if parsed_max_retries is not None:
|
|
319
|
+
overrides["max_retries"] = parsed_max_retries
|
|
320
|
+
if extra_kwargs:
|
|
321
|
+
overrides["extra_client_kwargs"] = extra_kwargs
|
|
322
|
+
|
|
323
|
+
try:
|
|
324
|
+
return OpenAISettings.from_env(dotenv_path=dotenv_path, **overrides)
|
|
325
|
+
except ValueError as exc:
|
|
326
|
+
raise ValueError(f"Failed to build OpenAI settings: {exc}") from exc
|
|
@@ -11,7 +11,7 @@ from contextlib import asynccontextmanager
|
|
|
11
11
|
from types import TracebackType
|
|
12
12
|
from typing import Any, AsyncIterator, Generic, Optional, TypeVar
|
|
13
13
|
|
|
14
|
-
from openai_sdk_helpers.
|
|
14
|
+
from openai_sdk_helpers.logging_config import log
|
|
15
15
|
|
|
16
16
|
T = TypeVar("T")
|
|
17
17
|
|