Skip to content

Commit 68e4317

Browse files
fix(api): avoid loading unrelated API resources on first use (openai#3898)
Importing the SDK or accessing one API resource currently loads unrelated resources, including Assistants. Load resource exports and streaming helpers when first accessed to reduce startup work. Existing import paths, class identity, static typing, wildcard imports, and discovery are preserved. Code that inspects module dictionaries directly may observe exports appearing later; import work and import errors move to first access. Regression tests cover import isolation, public exports, runtime annotations, pickling, reloads, sync/async requests and streams, and concurrent first access. Endpoint methods, models, dependencies, and webhook behavior are unchanged. Validation: 682 focused tests passed (14 skipped), along with Mypy, Pyright, and Ruff. Python 3.10/3.14, HTTPX2, build, lint, and baseline checks passed on the final revision. End-to-end testing also passed. Co-authored-by: marcuswood-oai <323319819+marcuswood-oai@users.noreply.github.com>
1 parent dcbd6b8 commit 68e4317

44 files changed

Lines changed: 3536 additions & 1170 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.castiron.stats.yml‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
schema_version: 1
2-
generation_id: 5ca8ce21-7fe9-4009-836d-1c94c1c1e116
2+
generation_id: 84d5b19e-e118-402a-940c-00134d57a039
33
openapi_spec_hash: 9530fc18efeeb0be58821616af20494d
44
openapi_transformed_spec_hash: bcd481fc6089377839837e55cdfa80cc
55
config_hash: 487f26152d0893506c0e0d1060441bd0
6-
codegen_sha: da44ef0149596e5412c15e70836dd015824a9a90
7-
codegen_hash: 838f201dfcf9313561fe25f2783c231d707b30a12cf93d0e54aec3687399b0ea
8-
public_codegen_sha: 8b18351582d0b9a4c85a147b4e8769630610fdeb
6+
codegen_sha: 136c93db5472eb81849571ca9059367e650ef723
7+
codegen_hash: 0a9ede8d6058e352650cb2411de35dc1509646d728f9a94c78531c177508d37e
8+
public_codegen_sha: 2fbff3f92f139fde3a75d38e399be22ad19afe04

‎src/openai/__init__.py‎

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,27 @@
108108
from .lib.azure import AzureOpenAI as AzureOpenAI, AsyncAzureOpenAI as AsyncAzureOpenAI
109109
from .lib.bedrock import BedrockOpenAI as BedrockOpenAI, AsyncBedrockOpenAI as AsyncBedrockOpenAI
110110
from .lib._old_api import *
111-
from .lib.streaming import (
112-
AssistantEventHandler as AssistantEventHandler,
113-
AsyncAssistantEventHandler as AsyncAssistantEventHandler,
114-
)
111+
112+
if _t.TYPE_CHECKING:
113+
from .lib.streaming import (
114+
AssistantEventHandler as AssistantEventHandler,
115+
AsyncAssistantEventHandler as AsyncAssistantEventHandler,
116+
)
117+
else:
118+
_STREAMING_EXPORTS = ("AssistantEventHandler", "AsyncAssistantEventHandler")
119+
120+
def __getattr__(name: str) -> _t.Any:
121+
if name in _STREAMING_EXPORTS:
122+
from importlib import import_module
123+
124+
value = getattr(import_module(".lib.streaming", __name__), name)
125+
globals()[name] = value
126+
return value
127+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
128+
129+
def __dir__() -> list[str]:
130+
return sorted(set(globals()) | set(_STREAMING_EXPORTS))
131+
115132

116133
_setup_logging()
117134

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,34 @@
1-
from ._assistants import (
2-
AssistantEventHandler as AssistantEventHandler,
3-
AssistantEventHandlerT as AssistantEventHandlerT,
4-
AssistantStreamManager as AssistantStreamManager,
5-
AsyncAssistantEventHandler as AsyncAssistantEventHandler,
6-
AsyncAssistantEventHandlerT as AsyncAssistantEventHandlerT,
7-
AsyncAssistantStreamManager as AsyncAssistantStreamManager,
8-
)
1+
import typing as _t
2+
3+
if _t.TYPE_CHECKING:
4+
from ._assistants import (
5+
AssistantEventHandler as AssistantEventHandler,
6+
AssistantEventHandlerT as AssistantEventHandlerT,
7+
AssistantStreamManager as AssistantStreamManager,
8+
AsyncAssistantEventHandler as AsyncAssistantEventHandler,
9+
AsyncAssistantEventHandlerT as AsyncAssistantEventHandlerT,
10+
AsyncAssistantStreamManager as AsyncAssistantStreamManager,
11+
)
12+
else:
13+
14+
def __getattr__(name: str) -> _t.Any:
15+
if name in __all__:
16+
from importlib import import_module
17+
18+
value = getattr(import_module("._assistants", __name__), name)
19+
globals()[name] = value
20+
return value
21+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
22+
23+
def __dir__() -> list[str]:
24+
return sorted(set(globals()) | set(__all__))
25+
26+
27+
__all__ = [
28+
"AssistantEventHandler",
29+
"AssistantEventHandlerT",
30+
"AssistantStreamManager",
31+
"AsyncAssistantEventHandler",
32+
"AsyncAssistantEventHandlerT",
33+
"AsyncAssistantStreamManager",
34+
]

0 commit comments

Comments
 (0)