mirror of
https://github.com/hwchase17/langchain.git
synced 2025-09-23 11:30:37 +00:00
core[performance]: use custom __getattr__
in __init__.py
files for lazy imports (#30769)
Most easily reviewed with the "hide whitespace" option toggled. Seeing 10-50% speed ups in import time for common structures 🚀 The general purpose of this PR is to lazily import structures within `langchain_core.XXX_module.__init__.py` so that we're not eagerly importing expensive dependencies (`pydantic`, `requests`, etc). Analysis of flamegraphs generated with `importtime` motivated these changes. For example, the one below demonstrates that importing `HumanMessage` accidentally triggered imports for `importlib.metadata`, `requests`, etc. There's still much more to do on this front, and we can start digging into our own internal code for optimizations now that we're less concerned about external imports. <img width="1210" alt="Screenshot 2025-04-11 at 1 10 54 PM" src="https://github.com/user-attachments/assets/112a3fe7-24a9-4294-92c1-d5ae64df839e" /> I've tracked the improvements with some local benchmarks: ## `pytest-benchmark` results | Name | Before (s) | After (s) | Delta (s) | % Change | |-----------------------------|------------|-----------|-----------|----------| | Document | 2.8683 | 1.2775 | -1.5908 | -55.46% | | HumanMessage | 2.2358 | 1.1673 | -1.0685 | -47.79% | | ChatPromptTemplate | 5.5235 | 2.9709 | -2.5526 | -46.22% | | Runnable | 2.9423 | 1.7793 | -1.163 | -39.53% | | InMemoryVectorStore | 3.1180 | 1.8417 | -1.2763 | -40.93% | | RunnableLambda | 2.7385 | 1.8745 | -0.864 | -31.55% | | tool | 5.1231 | 4.0771 | -1.046 | -20.42% | | CallbackManager | 4.2263 | 3.4099 | -0.8164 | -19.32% | | LangChainTracer | 3.8394 | 3.3101 | -0.5293 | -13.79% | | BaseChatModel | 4.3317 | 3.8806 | -0.4511 | -10.41% | | PydanticOutputParser | 3.2036 | 3.2995 | 0.0959 | 2.99% | | InMemoryRateLimiter | 0.5311 | 0.5995 | 0.0684 | 12.88% | Note the lack of change for `InMemoryRateLimiter` and `PydanticOutputParser` is just random noise, I'm getting comparable numbers locally. ## Local CodSpeed results We're still working on configuring CodSpeed on CI. The local usage produced similar results.
This commit is contained in:
@@ -25,35 +25,41 @@ from multiple components and prompt values. Prompt classes and functions make co
|
||||
|
||||
""" # noqa: E501
|
||||
|
||||
from langchain_core.prompts.base import (
|
||||
BasePromptTemplate,
|
||||
aformat_document,
|
||||
format_document,
|
||||
)
|
||||
from langchain_core.prompts.chat import (
|
||||
AIMessagePromptTemplate,
|
||||
BaseChatPromptTemplate,
|
||||
ChatMessagePromptTemplate,
|
||||
ChatPromptTemplate,
|
||||
HumanMessagePromptTemplate,
|
||||
MessagesPlaceholder,
|
||||
SystemMessagePromptTemplate,
|
||||
)
|
||||
from langchain_core.prompts.few_shot import (
|
||||
FewShotChatMessagePromptTemplate,
|
||||
FewShotPromptTemplate,
|
||||
)
|
||||
from langchain_core.prompts.few_shot_with_templates import FewShotPromptWithTemplates
|
||||
from langchain_core.prompts.loading import load_prompt
|
||||
from langchain_core.prompts.pipeline import PipelinePromptTemplate
|
||||
from langchain_core.prompts.prompt import PromptTemplate
|
||||
from langchain_core.prompts.string import (
|
||||
StringPromptTemplate,
|
||||
check_valid_template,
|
||||
get_template_variables,
|
||||
jinja2_formatter,
|
||||
validate_jinja2,
|
||||
)
|
||||
from importlib import import_module
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langchain_core.prompts.base import (
|
||||
BasePromptTemplate,
|
||||
aformat_document,
|
||||
format_document,
|
||||
)
|
||||
from langchain_core.prompts.chat import (
|
||||
AIMessagePromptTemplate,
|
||||
BaseChatPromptTemplate,
|
||||
ChatMessagePromptTemplate,
|
||||
ChatPromptTemplate,
|
||||
HumanMessagePromptTemplate,
|
||||
MessagesPlaceholder,
|
||||
SystemMessagePromptTemplate,
|
||||
)
|
||||
from langchain_core.prompts.few_shot import (
|
||||
FewShotChatMessagePromptTemplate,
|
||||
FewShotPromptTemplate,
|
||||
)
|
||||
from langchain_core.prompts.few_shot_with_templates import (
|
||||
FewShotPromptWithTemplates,
|
||||
)
|
||||
from langchain_core.prompts.loading import load_prompt
|
||||
from langchain_core.prompts.pipeline import PipelinePromptTemplate
|
||||
from langchain_core.prompts.prompt import PromptTemplate
|
||||
from langchain_core.prompts.string import (
|
||||
StringPromptTemplate,
|
||||
check_valid_template,
|
||||
get_template_variables,
|
||||
jinja2_formatter,
|
||||
validate_jinja2,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"AIMessagePromptTemplate",
|
||||
@@ -78,3 +84,43 @@ __all__ = [
|
||||
"jinja2_formatter",
|
||||
"validate_jinja2",
|
||||
]
|
||||
|
||||
_dynamic_imports = {
|
||||
"BasePromptTemplate": "base",
|
||||
"format_document": "base",
|
||||
"aformat_document": "base",
|
||||
"AIMessagePromptTemplate": "chat",
|
||||
"BaseChatPromptTemplate": "chat",
|
||||
"ChatMessagePromptTemplate": "chat",
|
||||
"ChatPromptTemplate": "chat",
|
||||
"HumanMessagePromptTemplate": "chat",
|
||||
"MessagesPlaceholder": "chat",
|
||||
"SystemMessagePromptTemplate": "chat",
|
||||
"FewShotChatMessagePromptTemplate": "few_shot",
|
||||
"FewShotPromptTemplate": "few_shot",
|
||||
"FewShotPromptWithTemplates": "few_shot_with_templates",
|
||||
"load_prompt": "loading",
|
||||
"PipelinePromptTemplate": "pipeline",
|
||||
"PromptTemplate": "prompt",
|
||||
"StringPromptTemplate": "string",
|
||||
"check_valid_template": "string",
|
||||
"get_template_variables": "string",
|
||||
"jinja2_formatter": "string",
|
||||
"validate_jinja2": "string",
|
||||
}
|
||||
|
||||
|
||||
def __getattr__(attr_name: str) -> object:
|
||||
module_name = _dynamic_imports.get(attr_name)
|
||||
package = __spec__.parent # type: ignore[name-defined]
|
||||
if module_name == "__module__" or module_name is None:
|
||||
result = import_module(f".{attr_name}", package=package)
|
||||
else:
|
||||
module = import_module(f".{module_name}", package=package)
|
||||
result = getattr(module, attr_name)
|
||||
globals()[attr_name] = result
|
||||
return result
|
||||
|
||||
|
||||
def __dir__() -> list[str]:
|
||||
return list(__all__)
|
||||
|
Reference in New Issue
Block a user