Skip to content

perf: lazy load top-level instructor exports#2264

Open
jxnl wants to merge 2 commits intomainfrom
codex/import-ram-2205
Open

perf: lazy load top-level instructor exports#2264
jxnl wants to merge 2 commits intomainfrom
codex/import-ram-2205

Conversation

@jxnl
Copy link
Copy Markdown
Collaborator

@jxnl jxnl commented Apr 12, 2026

Fixes #2205.

Summary

  • make instructor.__init__, instructor.core, and instructor.processing lazy so bare import instructor does not eagerly import patching, response processing, and provider helpers
  • keep the public top-level API intact by resolving exports on first attribute access
  • add a subprocess regression test that proves bare import does not eagerly load instructor.processing.response or provider util modules
  • narrow instructor.processing.response to import exceptions without re-entering eager core package initialization

Verification

  • uv run pytest tests/test_import_lazy_loading.py tests/test_patch.py tests/test_process_response.py tests/test_xai_optional_dependency.py -q
  • uv run ruff check instructor/__init__.py instructor/core/__init__.py instructor/processing/__init__.py instructor/processing/response.py tests/test_import_lazy_loading.py
  • measured in this local worktree environment:
    • before: import instructor loaded about 99 MB RSS / 1161 modules
    • after: import instructor loaded about 15 MB RSS / 57 modules

Notes

  • I also sampled a broader import-sensitive test set. The remaining failures in tests/test_auto_client.py and provider-specific response conversion tests were environment-dependent missing-provider/live-provider issues, not regressions from this lazy import change.

Note

Medium Risk
Changes package import mechanics via __getattr__-based lazy exports, which can subtly affect runtime import order, optional dependency detection, and attribute resolution. Risk is mitigated by regression tests but could surface in edge-case import patterns.

Overview
Switches instructor, instructor.core, and instructor.processing to lazy-load public exports via module-level __getattr__/__dir__, avoiding eager imports of patching/response/provider code on bare import instructor while keeping the same top-level API surface.

Refactors optional provider exports (e.g. from_anthropic, from_gemini, etc.) to register as lazy attributes only when dependencies are present, and adjusts processing.response to import exceptions directly from ..core.exceptions to prevent triggering core package initialization.

Adds a subprocess-based regression test (tests/test_import_lazy_loading.py) ensuring heavy modules aren’t imported eagerly and that accessing a top-level export (e.g. instructor.patch) triggers the expected on-demand import.

Reviewed by Cursor Bugbot for commit b743282. Configure here.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Apr 12, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
❌ Deployment failed
View logs
instructor 8ecccdc Apr 12 2026, 02:58 AM

@github-actions github-actions Bot added enhancement New feature or request python Pull requests that update python code size:L This PR changes 100-499 lines, ignoring generated files. status:pending-merge Related PR is pending merge labels Apr 12, 2026
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Lazy hooks resolution fails via getattr on submodule
    • Updated the lazy attribute mapping so instructor.hooks imports the instructor.core.hooks submodule directly instead of using getattr(instructor.core, "hooks").

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit b743282. Configure here.

Comment thread instructor/__init__.py Outdated
"AsyncInstructor": (".core.client", "AsyncInstructor"),
"from_openai": (".core.client", "from_openai"),
"from_litellm": (".core.client", "from_litellm"),
"hooks": (".core", "hooks"),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lazy hooks resolution fails via getattr on submodule

High Severity

The lazy entry "hooks": (".core", "hooks") tries to resolve instructor.hooks by importing instructor.core and then calling getattr(module, "hooks"). However, instructor.core.__getattr__ only handles names in its _LAZY_ATTRS dict, which contains "Hooks" and "HookName" but not the submodule name "hooks". Unlike the from .core import hooks import statement (which has a submodule fallback), getattr on a module with __getattr__ does not attempt submodule resolution per PEP 562. This causes AttributeError when accessing instructor.hooks unless another lazy attribute has already triggered importing instructor.core.hooks as a side effect. The mapping needs to be (".core.hooks", None) to directly import the submodule.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b743282. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request python Pull requests that update python code size:L This PR changes 100-499 lines, ignoring generated files. status:pending-merge Related PR is pending merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RAM Spike when I import instructor

2 participants