Skip to content

perf(lib): stop pulling openai.types.beta into every import openai - #3727

Open
aviseth wants to merge 1 commit into
openai:mainfrom
aviseth:perf/lazy-beta-types-in-assistant-streaming
Open

perf(lib): stop pulling openai.types.beta into every import openai#3727
aviseth wants to merge 1 commit into
openai:mainfrom
aviseth:perf/lazy-beta-types-in-assistant-streaming

Conversation

@aviseth

@aviseth aviseth commented Aug 24, 2026

Copy link
Copy Markdown
  • I understand that this repository is auto-generated and my pull request may not be merged

Changes being requested

openai/__init__.py imports openai.lib.streaming, and openai/lib/streaming/_assistants.py imported openai.types.beta at module scope. That loaded 318 modules on every import openai — about 30% of the total import cost — so that anyone importing the package paid for the Assistants API whether or not they used it.

_assistants.py already has from __future__ import annotations, so all but two of those names were needed only by type checkers. This moves them under TYPE_CHECKING and imports the two runtime exceptions locally: RunStep, used by construct_type(type_=RunStep, ...) in accumulate_run_step, and MessageContent, used twice in accumulate_event.

Measured from a clean checkout with the repo venv, 25 cold runs each on Python 3.13:

sys.modules openai.types.beta wall, median
main 1234 318 359.2 ms
this branch 916 0 256.7 ms

1.40x faster, −102 ms, −318 modules.

There is no public API change. openai.AssistantEventHandler and openai.AsyncAssistantEventHandler remain eagerly exported from openai/__init__.py, from openai import * is unchanged, and import openai.lib.streaming still works. This deliberately avoids a module-level __getattr__ on the package.

The whole change is confined to src/openai/lib/, which CONTRIBUTING.md says the generator never modifies and which carries no File generated from our OpenAPI spec header. It therefore does not touch generated output, does not consume Castiron custom-code budget, and cannot conflict with a regen — which is the concern that closed #2950.

tests/lib/test_streaming_lazy_types.py asserts in a fresh interpreter that import openai loads no openai.types.beta module, and that both handler names are still eagerly exported. It fails on main and passes here.

./scripts/lint is clean (ruff, pyright strict, mypy 1573 files) and ./scripts/test passes both lanes: 9127 passed / 144 skipped on Pydantic v2, 9113 passed / 158 skipped on Pydantic v1.

Additional context & links

Fixes the bulk of #2819. Supersedes the approach in #2950, which deferred openai.lib itself — that is only 0.9% of import cost, which is why it measured ~4%. The cost was in what one lib module pulled in behind it, not in lib.

The remaining openai.types.* non-beta cost (~144 ms across 434 modules) is reachable from openai/__init__.py directly and would need a change to generated code, so it is out of scope here.

openai/__init__.py imports openai.lib.streaming, whose _assistants module
imported openai.types.beta at module scope. That loaded 318 modules on every
import openai, for annotations that from __future__ import annotations already
defers and two runtime paths that can import locally.

Move the annotation-only imports under TYPE_CHECKING and import RunStep and
MessageContent inside the two functions that construct them at runtime.

import openai drops from 1234 to 916 modules and from 359ms to 257ms median
cold import (1.40x, python 3.13, 25 runs). No public API change:
openai.AssistantEventHandler and openai.AsyncAssistantEventHandler stay
eagerly exported.

Refs openai#2819
@aviseth
aviseth requested a review from a team as a code owner August 24, 2026 21:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant