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.
Files changed (44) hide show
  1. openai_sdk_helpers/__init__.py +44 -7
  2. openai_sdk_helpers/agent/base.py +5 -1
  3. openai_sdk_helpers/agent/coordination.py +4 -5
  4. openai_sdk_helpers/agent/runner.py +4 -1
  5. openai_sdk_helpers/agent/search/base.py +1 -0
  6. openai_sdk_helpers/agent/search/vector.py +2 -0
  7. openai_sdk_helpers/cli.py +265 -0
  8. openai_sdk_helpers/config.py +93 -2
  9. openai_sdk_helpers/context_manager.py +1 -1
  10. openai_sdk_helpers/deprecation.py +167 -0
  11. openai_sdk_helpers/environment.py +3 -2
  12. openai_sdk_helpers/errors.py +0 -12
  13. openai_sdk_helpers/files_api.py +373 -0
  14. openai_sdk_helpers/logging_config.py +24 -95
  15. openai_sdk_helpers/prompt/base.py +1 -1
  16. openai_sdk_helpers/response/__init__.py +7 -3
  17. openai_sdk_helpers/response/base.py +217 -147
  18. openai_sdk_helpers/response/config.py +16 -1
  19. openai_sdk_helpers/response/files.py +392 -0
  20. openai_sdk_helpers/response/messages.py +1 -0
  21. openai_sdk_helpers/retry.py +1 -1
  22. openai_sdk_helpers/streamlit_app/app.py +97 -7
  23. openai_sdk_helpers/streamlit_app/streamlit_web_search.py +15 -8
  24. openai_sdk_helpers/structure/base.py +6 -6
  25. openai_sdk_helpers/structure/plan/helpers.py +1 -0
  26. openai_sdk_helpers/structure/plan/task.py +7 -7
  27. openai_sdk_helpers/tools.py +116 -13
  28. openai_sdk_helpers/utils/__init__.py +100 -35
  29. openai_sdk_helpers/{async_utils.py → utils/async_utils.py} +5 -6
  30. openai_sdk_helpers/utils/coercion.py +138 -0
  31. openai_sdk_helpers/utils/deprecation.py +167 -0
  32. openai_sdk_helpers/utils/encoding.py +189 -0
  33. openai_sdk_helpers/utils/json_utils.py +98 -0
  34. openai_sdk_helpers/utils/output_validation.py +448 -0
  35. openai_sdk_helpers/utils/path_utils.py +46 -0
  36. openai_sdk_helpers/{validation.py → utils/validation.py} +7 -3
  37. openai_sdk_helpers/vector_storage/storage.py +59 -28
  38. {openai_sdk_helpers-0.1.0.dist-info → openai_sdk_helpers-0.1.2.dist-info}/METADATA +152 -3
  39. openai_sdk_helpers-0.1.2.dist-info/RECORD +79 -0
  40. openai_sdk_helpers-0.1.2.dist-info/entry_points.txt +2 -0
  41. openai_sdk_helpers/utils/core.py +0 -596
  42. openai_sdk_helpers-0.1.0.dist-info/RECORD +0 -69
  43. {openai_sdk_helpers-0.1.0.dist-info → openai_sdk_helpers-0.1.2.dist-info}/WHEEL +0 -0
  44. {openai_sdk_helpers-0.1.0.dist-info → openai_sdk_helpers-0.1.2.dist-info}/licenses/LICENSE +0 -0
@@ -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 .utils import (
83
- build_openai_settings,
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
  ]
@@ -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 ..environment import DATETIME_FMT
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(DATETIME_FMT)
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.mkdir(parents=True, exist_ok=True)
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:
@@ -117,6 +117,7 @@ class SearchToolAgent(AgentBase, Generic[ItemType, ResultType, PlanType]):
117
117
 
118
118
  def __init__(
119
119
  self,
120
+ *,
120
121
  prompt_dir: Optional[Path] = None,
121
122
  default_model: Optional[str] = None,
122
123
  max_concurrent_searches: int = 10,
@@ -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())
@@ -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.utils.core import log
14
+ from openai_sdk_helpers.logging_config import log
15
15
 
16
16
  T = TypeVar("T")
17
17