From 4f69094b51bd1d637c15433050b6a793202354c4 Mon Sep 17 00:00:00 2001
From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com>
Date: Mon, 14 Apr 2025 08:57:54 -0400
Subject: [PATCH] core[performance]: use custom `__getattr__` in `__init__.py`
files for lazy imports (#30769)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
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.
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.
---
libs/core/langchain_core/_api/__init__.py | 62 ++++++--
.../core/langchain_core/callbacks/__init__.py | 137 +++++++++++++-----
.../document_loaders/__init__.py | 35 ++++-
.../core/langchain_core/documents/__init__.py | 32 +++-
.../langchain_core/embeddings/__init__.py | 33 ++++-
.../example_selectors/__init__.py | 46 ++++--
libs/core/langchain_core/indexing/__init__.py | 47 +++++-
.../language_models/__init__.py | 76 +++++++---
libs/core/langchain_core/load/__init__.py | 34 ++++-
libs/core/langchain_core/messages/__init__.py | 127 +++++++++++-----
.../langchain_core/output_parsers/__init__.py | 91 +++++++++---
libs/core/langchain_core/outputs/__init__.py | 43 +++++-
libs/core/langchain_core/prompts/__init__.py | 104 +++++++++----
.../core/langchain_core/runnables/__init__.py | 124 +++++++++++-----
libs/core/langchain_core/tools/__init__.py | 96 ++++++++----
libs/core/langchain_core/tracers/__init__.py | 51 +++++--
libs/core/langchain_core/utils/__init__.py | 103 +++++++++----
17 files changed, 949 insertions(+), 292 deletions(-)
diff --git a/libs/core/langchain_core/_api/__init__.py b/libs/core/langchain_core/_api/__init__.py
index 2251973d6b5..9379f2c4cad 100644
--- a/libs/core/langchain_core/_api/__init__.py
+++ b/libs/core/langchain_core/_api/__init__.py
@@ -9,20 +9,24 @@ This module is only relevant for LangChain developers, not for users.
"""
-from .beta_decorator import (
- LangChainBetaWarning,
- beta,
- suppress_langchain_beta_warning,
- surface_langchain_beta_warnings,
-)
-from .deprecation import (
- LangChainDeprecationWarning,
- deprecated,
- suppress_langchain_deprecation_warning,
- surface_langchain_deprecation_warnings,
- warn_deprecated,
-)
-from .path import as_import_path, get_relative_path
+from importlib import import_module
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from .beta_decorator import (
+ LangChainBetaWarning,
+ beta,
+ suppress_langchain_beta_warning,
+ surface_langchain_beta_warnings,
+ )
+ from .deprecation import (
+ LangChainDeprecationWarning,
+ deprecated,
+ suppress_langchain_deprecation_warning,
+ surface_langchain_deprecation_warnings,
+ warn_deprecated,
+ )
+ from .path import as_import_path, get_relative_path
__all__ = [
"as_import_path",
@@ -37,3 +41,33 @@ __all__ = [
"surface_langchain_deprecation_warnings",
"warn_deprecated",
]
+
+_dynamic_imports = {
+ "LangChainBetaWarning": "beta_decorator",
+ "beta": "beta_decorator",
+ "suppress_langchain_beta_warning": "beta_decorator",
+ "surface_langchain_beta_warnings": "beta_decorator",
+ "as_import_path": "path",
+ "get_relative_path": "path",
+ "LangChainDeprecationWarning": "deprecation",
+ "deprecated": "deprecation",
+ "surface_langchain_deprecation_warnings": "deprecation",
+ "suppress_langchain_deprecation_warning": "deprecation",
+ "warn_deprecated": "deprecation",
+}
+
+
+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__)
diff --git a/libs/core/langchain_core/callbacks/__init__.py b/libs/core/langchain_core/callbacks/__init__.py
index eca2b14feb5..23758ec9d55 100644
--- a/libs/core/langchain_core/callbacks/__init__.py
+++ b/libs/core/langchain_core/callbacks/__init__.py
@@ -7,46 +7,50 @@
BaseCallbackHandler --> CallbackHandler # Example: AimCallbackHandler
"""
-from langchain_core.callbacks.base import (
- AsyncCallbackHandler,
- BaseCallbackHandler,
- BaseCallbackManager,
- CallbackManagerMixin,
- Callbacks,
- ChainManagerMixin,
- LLMManagerMixin,
- RetrieverManagerMixin,
- RunManagerMixin,
- ToolManagerMixin,
-)
-from langchain_core.callbacks.file import FileCallbackHandler
-from langchain_core.callbacks.manager import (
- AsyncCallbackManager,
- AsyncCallbackManagerForChainGroup,
- AsyncCallbackManagerForChainRun,
- AsyncCallbackManagerForLLMRun,
- AsyncCallbackManagerForRetrieverRun,
- AsyncCallbackManagerForToolRun,
- AsyncParentRunManager,
- AsyncRunManager,
- BaseRunManager,
- CallbackManager,
- CallbackManagerForChainGroup,
- CallbackManagerForChainRun,
- CallbackManagerForLLMRun,
- CallbackManagerForRetrieverRun,
- CallbackManagerForToolRun,
- ParentRunManager,
- RunManager,
- adispatch_custom_event,
- dispatch_custom_event,
-)
-from langchain_core.callbacks.stdout import StdOutCallbackHandler
-from langchain_core.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
-from langchain_core.callbacks.usage import (
- UsageMetadataCallbackHandler,
- get_usage_metadata_callback,
-)
+from importlib import import_module
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from langchain_core.callbacks.base import (
+ AsyncCallbackHandler,
+ BaseCallbackHandler,
+ BaseCallbackManager,
+ CallbackManagerMixin,
+ Callbacks,
+ ChainManagerMixin,
+ LLMManagerMixin,
+ RetrieverManagerMixin,
+ RunManagerMixin,
+ ToolManagerMixin,
+ )
+ from langchain_core.callbacks.file import FileCallbackHandler
+ from langchain_core.callbacks.manager import (
+ AsyncCallbackManager,
+ AsyncCallbackManagerForChainGroup,
+ AsyncCallbackManagerForChainRun,
+ AsyncCallbackManagerForLLMRun,
+ AsyncCallbackManagerForRetrieverRun,
+ AsyncCallbackManagerForToolRun,
+ AsyncParentRunManager,
+ AsyncRunManager,
+ BaseRunManager,
+ CallbackManager,
+ CallbackManagerForChainGroup,
+ CallbackManagerForChainRun,
+ CallbackManagerForLLMRun,
+ CallbackManagerForRetrieverRun,
+ CallbackManagerForToolRun,
+ ParentRunManager,
+ RunManager,
+ adispatch_custom_event,
+ dispatch_custom_event,
+ )
+ from langchain_core.callbacks.stdout import StdOutCallbackHandler
+ from langchain_core.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
+ from langchain_core.callbacks.usage import (
+ UsageMetadataCallbackHandler,
+ get_usage_metadata_callback,
+ )
__all__ = [
"dispatch_custom_event",
@@ -84,3 +88,56 @@ __all__ = [
"UsageMetadataCallbackHandler",
"get_usage_metadata_callback",
]
+
+_dynamic_imports = {
+ "AsyncCallbackHandler": "base",
+ "BaseCallbackHandler": "base",
+ "BaseCallbackManager": "base",
+ "CallbackManagerMixin": "base",
+ "Callbacks": "base",
+ "ChainManagerMixin": "base",
+ "LLMManagerMixin": "base",
+ "RetrieverManagerMixin": "base",
+ "RunManagerMixin": "base",
+ "ToolManagerMixin": "base",
+ "FileCallbackHandler": "file",
+ "AsyncCallbackManager": "manager",
+ "AsyncCallbackManagerForChainGroup": "manager",
+ "AsyncCallbackManagerForChainRun": "manager",
+ "AsyncCallbackManagerForLLMRun": "manager",
+ "AsyncCallbackManagerForRetrieverRun": "manager",
+ "AsyncCallbackManagerForToolRun": "manager",
+ "AsyncParentRunManager": "manager",
+ "AsyncRunManager": "manager",
+ "BaseRunManager": "manager",
+ "CallbackManager": "manager",
+ "CallbackManagerForChainGroup": "manager",
+ "CallbackManagerForChainRun": "manager",
+ "CallbackManagerForLLMRun": "manager",
+ "CallbackManagerForRetrieverRun": "manager",
+ "CallbackManagerForToolRun": "manager",
+ "ParentRunManager": "manager",
+ "RunManager": "manager",
+ "adispatch_custom_event": "manager",
+ "dispatch_custom_event": "manager",
+ "StdOutCallbackHandler": "stdout",
+ "StreamingStdOutCallbackHandler": "streaming_stdout",
+ "UsageMetadataCallbackHandler": "usage",
+ "get_usage_metadata_callback": "usage",
+}
+
+
+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__)
diff --git a/libs/core/langchain_core/document_loaders/__init__.py b/libs/core/langchain_core/document_loaders/__init__.py
index f85bcc454e9..ba81ea2578c 100644
--- a/libs/core/langchain_core/document_loaders/__init__.py
+++ b/libs/core/langchain_core/document_loaders/__init__.py
@@ -1,8 +1,12 @@
"""Document loaders."""
-from langchain_core.document_loaders.base import BaseBlobParser, BaseLoader
-from langchain_core.document_loaders.blob_loaders import Blob, BlobLoader, PathLike
-from langchain_core.document_loaders.langsmith import LangSmithLoader
+from importlib import import_module
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from langchain_core.document_loaders.base import BaseBlobParser, BaseLoader
+ from langchain_core.document_loaders.blob_loaders import Blob, BlobLoader, PathLike
+ from langchain_core.document_loaders.langsmith import LangSmithLoader
__all__ = [
"BaseBlobParser",
@@ -12,3 +16,28 @@ __all__ = [
"PathLike",
"LangSmithLoader",
]
+
+_dynamic_imports = {
+ "BaseBlobParser": "base",
+ "BaseLoader": "base",
+ "Blob": "blob_loaders",
+ "BlobLoader": "blob_loaders",
+ "PathLike": "blob_loaders",
+ "LangSmithLoader": "langsmith",
+}
+
+
+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__)
diff --git a/libs/core/langchain_core/documents/__init__.py b/libs/core/langchain_core/documents/__init__.py
index 1d9b3d63893..e3d7f740687 100644
--- a/libs/core/langchain_core/documents/__init__.py
+++ b/libs/core/langchain_core/documents/__init__.py
@@ -5,8 +5,34 @@ and their transformations.
"""
-from langchain_core.documents.base import Document
-from langchain_core.documents.compressor import BaseDocumentCompressor
-from langchain_core.documents.transformers import BaseDocumentTransformer
+from importlib import import_module
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from .base import Document
+ from .compressor import BaseDocumentCompressor
+ from .transformers import BaseDocumentTransformer
__all__ = ["Document", "BaseDocumentTransformer", "BaseDocumentCompressor"]
+
+_dynamic_imports = {
+ "Document": "base",
+ "BaseDocumentCompressor": "compressor",
+ "BaseDocumentTransformer": "transformers",
+}
+
+
+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__)
diff --git a/libs/core/langchain_core/embeddings/__init__.py b/libs/core/langchain_core/embeddings/__init__.py
index 2ef05597db0..e81f47eabe0 100644
--- a/libs/core/langchain_core/embeddings/__init__.py
+++ b/libs/core/langchain_core/embeddings/__init__.py
@@ -1,6 +1,35 @@
"""Embeddings."""
-from langchain_core.embeddings.embeddings import Embeddings
-from langchain_core.embeddings.fake import DeterministicFakeEmbedding, FakeEmbeddings
+from importlib import import_module
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from langchain_core.embeddings.embeddings import Embeddings
+ from langchain_core.embeddings.fake import (
+ DeterministicFakeEmbedding,
+ FakeEmbeddings,
+ )
__all__ = ["DeterministicFakeEmbedding", "Embeddings", "FakeEmbeddings"]
+
+_dynamic_imports = {
+ "Embeddings": "embeddings",
+ "DeterministicFakeEmbedding": "fake",
+ "FakeEmbeddings": "fake",
+}
+
+
+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__)
diff --git a/libs/core/langchain_core/example_selectors/__init__.py b/libs/core/langchain_core/example_selectors/__init__.py
index 68f454fd2de..5e4d0be41e2 100644
--- a/libs/core/langchain_core/example_selectors/__init__.py
+++ b/libs/core/langchain_core/example_selectors/__init__.py
@@ -4,15 +4,19 @@
This allows us to select examples that are most relevant to the input.
"""
-from langchain_core.example_selectors.base import BaseExampleSelector
-from langchain_core.example_selectors.length_based import (
- LengthBasedExampleSelector,
-)
-from langchain_core.example_selectors.semantic_similarity import (
- MaxMarginalRelevanceExampleSelector,
- SemanticSimilarityExampleSelector,
- sorted_values,
-)
+from importlib import import_module
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from langchain_core.example_selectors.base import BaseExampleSelector
+ from langchain_core.example_selectors.length_based import (
+ LengthBasedExampleSelector,
+ )
+ from langchain_core.example_selectors.semantic_similarity import (
+ MaxMarginalRelevanceExampleSelector,
+ SemanticSimilarityExampleSelector,
+ sorted_values,
+ )
__all__ = [
"BaseExampleSelector",
@@ -21,3 +25,27 @@ __all__ = [
"SemanticSimilarityExampleSelector",
"sorted_values",
]
+
+_dynamic_imports = {
+ "BaseExampleSelector": "base",
+ "LengthBasedExampleSelector": "length_based",
+ "MaxMarginalRelevanceExampleSelector": "semantic_similarity",
+ "SemanticSimilarityExampleSelector": "semantic_similarity",
+ "sorted_values": "semantic_similarity",
+}
+
+
+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__)
diff --git a/libs/core/langchain_core/indexing/__init__.py b/libs/core/langchain_core/indexing/__init__.py
index 472f41e11a8..17c4c1e8e5c 100644
--- a/libs/core/langchain_core/indexing/__init__.py
+++ b/libs/core/langchain_core/indexing/__init__.py
@@ -5,14 +5,18 @@ a vectorstore while avoiding duplicated content and over-writing content
if it's unchanged.
"""
-from langchain_core.indexing.api import IndexingResult, aindex, index
-from langchain_core.indexing.base import (
- DeleteResponse,
- DocumentIndex,
- InMemoryRecordManager,
- RecordManager,
- UpsertResponse,
-)
+from importlib import import_module
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from langchain_core.indexing.api import IndexingResult, aindex, index
+ from langchain_core.indexing.base import (
+ DeleteResponse,
+ DocumentIndex,
+ InMemoryRecordManager,
+ RecordManager,
+ UpsertResponse,
+ )
__all__ = [
"aindex",
@@ -24,3 +28,30 @@ __all__ = [
"RecordManager",
"UpsertResponse",
]
+
+_dynamic_imports = {
+ "aindex": "api",
+ "index": "api",
+ "IndexingResult": "api",
+ "DeleteResponse": "base",
+ "DocumentIndex": "base",
+ "InMemoryRecordManager": "base",
+ "RecordManager": "base",
+ "UpsertResponse": "base",
+}
+
+
+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__)
diff --git a/libs/core/langchain_core/language_models/__init__.py b/libs/core/langchain_core/language_models/__init__.py
index 37ac295314e..15de73012a3 100644
--- a/libs/core/langchain_core/language_models/__init__.py
+++ b/libs/core/langchain_core/language_models/__init__.py
@@ -41,23 +41,30 @@ https://python.langchain.com/docs/how_to/custom_llm/
""" # noqa: E501
-from langchain_core.language_models.base import (
- BaseLanguageModel,
- LangSmithParams,
- LanguageModelInput,
- LanguageModelLike,
- LanguageModelOutput,
- get_tokenizer,
-)
-from langchain_core.language_models.chat_models import BaseChatModel, SimpleChatModel
-from langchain_core.language_models.fake import FakeListLLM, FakeStreamingListLLM
-from langchain_core.language_models.fake_chat_models import (
- FakeListChatModel,
- FakeMessagesListChatModel,
- GenericFakeChatModel,
- ParrotFakeChatModel,
-)
-from langchain_core.language_models.llms import LLM, BaseLLM
+from importlib import import_module
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from langchain_core.language_models.base import (
+ BaseLanguageModel,
+ LangSmithParams,
+ LanguageModelInput,
+ LanguageModelLike,
+ LanguageModelOutput,
+ get_tokenizer,
+ )
+ from langchain_core.language_models.chat_models import (
+ BaseChatModel,
+ SimpleChatModel,
+ )
+ from langchain_core.language_models.fake import FakeListLLM, FakeStreamingListLLM
+ from langchain_core.language_models.fake_chat_models import (
+ FakeListChatModel,
+ FakeMessagesListChatModel,
+ GenericFakeChatModel,
+ ParrotFakeChatModel,
+ )
+ from langchain_core.language_models.llms import LLM, BaseLLM
__all__ = [
"BaseLanguageModel",
@@ -77,3 +84,38 @@ __all__ = [
"GenericFakeChatModel",
"ParrotFakeChatModel",
]
+
+_dynamic_imports = {
+ "BaseLanguageModel": "base",
+ "LangSmithParams": "base",
+ "LanguageModelInput": "base",
+ "LanguageModelLike": "base",
+ "LanguageModelOutput": "base",
+ "get_tokenizer": "base",
+ "BaseChatModel": "chat_models",
+ "SimpleChatModel": "chat_models",
+ "FakeListLLM": "fake",
+ "FakeStreamingListLLM": "fake",
+ "FakeListChatModel": "fake_chat_models",
+ "FakeMessagesListChatModel": "fake_chat_models",
+ "GenericFakeChatModel": "fake_chat_models",
+ "ParrotFakeChatModel": "fake_chat_models",
+ "LLM": "llms",
+ "BaseLLM": "llms",
+}
+
+
+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__)
diff --git a/libs/core/langchain_core/load/__init__.py b/libs/core/langchain_core/load/__init__.py
index 913bd392604..f678030bf35 100644
--- a/libs/core/langchain_core/load/__init__.py
+++ b/libs/core/langchain_core/load/__init__.py
@@ -1,7 +1,35 @@
"""**Load** module helps with serialization and deserialization."""
-from langchain_core.load.dump import dumpd, dumps
-from langchain_core.load.load import load, loads
-from langchain_core.load.serializable import Serializable
+from importlib import import_module
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from langchain_core.load.dump import dumpd, dumps
+ from langchain_core.load.load import load, loads
+ from langchain_core.load.serializable import Serializable
__all__ = ["dumpd", "dumps", "load", "loads", "Serializable"]
+
+_dynamic_imports = {
+ "dumpd": "dump",
+ "dumps": "dump",
+ "load": "load",
+ "loads": "load",
+ "Serializable": "serializable",
+}
+
+
+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__)
diff --git a/libs/core/langchain_core/messages/__init__.py b/libs/core/langchain_core/messages/__init__.py
index f8fdf4d4a3b..8fe8cd20389 100644
--- a/libs/core/langchain_core/messages/__init__.py
+++ b/libs/core/langchain_core/messages/__init__.py
@@ -15,42 +15,46 @@
""" # noqa: E501
-from langchain_core.messages.ai import (
- AIMessage,
- AIMessageChunk,
-)
-from langchain_core.messages.base import (
- BaseMessage,
- BaseMessageChunk,
- merge_content,
- message_to_dict,
- messages_to_dict,
-)
-from langchain_core.messages.chat import ChatMessage, ChatMessageChunk
-from langchain_core.messages.function import FunctionMessage, FunctionMessageChunk
-from langchain_core.messages.human import HumanMessage, HumanMessageChunk
-from langchain_core.messages.modifier import RemoveMessage
-from langchain_core.messages.system import SystemMessage, SystemMessageChunk
-from langchain_core.messages.tool import (
- InvalidToolCall,
- ToolCall,
- ToolCallChunk,
- ToolMessage,
- ToolMessageChunk,
-)
-from langchain_core.messages.utils import (
- AnyMessage,
- MessageLikeRepresentation,
- _message_from_dict,
- convert_to_messages,
- convert_to_openai_messages,
- filter_messages,
- get_buffer_string,
- merge_message_runs,
- message_chunk_to_message,
- messages_from_dict,
- trim_messages,
-)
+from importlib import import_module
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from langchain_core.messages.ai import (
+ AIMessage,
+ AIMessageChunk,
+ )
+ from langchain_core.messages.base import (
+ BaseMessage,
+ BaseMessageChunk,
+ merge_content,
+ message_to_dict,
+ messages_to_dict,
+ )
+ from langchain_core.messages.chat import ChatMessage, ChatMessageChunk
+ from langchain_core.messages.function import FunctionMessage, FunctionMessageChunk
+ from langchain_core.messages.human import HumanMessage, HumanMessageChunk
+ from langchain_core.messages.modifier import RemoveMessage
+ from langchain_core.messages.system import SystemMessage, SystemMessageChunk
+ from langchain_core.messages.tool import (
+ InvalidToolCall,
+ ToolCall,
+ ToolCallChunk,
+ ToolMessage,
+ ToolMessageChunk,
+ )
+ from langchain_core.messages.utils import (
+ AnyMessage,
+ MessageLikeRepresentation,
+ _message_from_dict,
+ convert_to_messages,
+ convert_to_openai_messages,
+ filter_messages,
+ get_buffer_string,
+ merge_message_runs,
+ message_chunk_to_message,
+ messages_from_dict,
+ trim_messages,
+ )
__all__ = [
"AIMessage",
@@ -86,3 +90,54 @@ __all__ = [
"trim_messages",
"convert_to_openai_messages",
]
+
+_dynamic_imports = {
+ "AIMessage": "ai",
+ "AIMessageChunk": "ai",
+ "BaseMessage": "base",
+ "BaseMessageChunk": "base",
+ "merge_content": "base",
+ "message_to_dict": "base",
+ "messages_to_dict": "base",
+ "ChatMessage": "chat",
+ "ChatMessageChunk": "chat",
+ "FunctionMessage": "function",
+ "FunctionMessageChunk": "function",
+ "HumanMessage": "human",
+ "HumanMessageChunk": "human",
+ "RemoveMessage": "modifier",
+ "SystemMessage": "system",
+ "SystemMessageChunk": "system",
+ "InvalidToolCall": "tool",
+ "ToolCall": "tool",
+ "ToolCallChunk": "tool",
+ "ToolMessage": "tool",
+ "ToolMessageChunk": "tool",
+ "AnyMessage": "utils",
+ "MessageLikeRepresentation": "utils",
+ "_message_from_dict": "utils",
+ "convert_to_messages": "utils",
+ "convert_to_openai_messages": "utils",
+ "filter_messages": "utils",
+ "get_buffer_string": "utils",
+ "merge_message_runs": "utils",
+ "message_chunk_to_message": "utils",
+ "messages_from_dict": "utils",
+ "trim_messages": "utils",
+}
+
+
+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__)
diff --git a/libs/core/langchain_core/output_parsers/__init__.py b/libs/core/langchain_core/output_parsers/__init__.py
index a1bbdeb4f9d..32c52cb617b 100644
--- a/libs/core/langchain_core/output_parsers/__init__.py
+++ b/libs/core/langchain_core/output_parsers/__init__.py
@@ -13,30 +13,37 @@
Serializable, Generation, PromptValue
""" # noqa: E501
-from langchain_core.output_parsers.base import (
- BaseGenerationOutputParser,
- BaseLLMOutputParser,
- BaseOutputParser,
-)
-from langchain_core.output_parsers.json import JsonOutputParser, SimpleJsonOutputParser
-from langchain_core.output_parsers.list import (
- CommaSeparatedListOutputParser,
- ListOutputParser,
- MarkdownListOutputParser,
- NumberedListOutputParser,
-)
-from langchain_core.output_parsers.openai_tools import (
- JsonOutputKeyToolsParser,
- JsonOutputToolsParser,
- PydanticToolsParser,
-)
-from langchain_core.output_parsers.pydantic import PydanticOutputParser
-from langchain_core.output_parsers.string import StrOutputParser
-from langchain_core.output_parsers.transform import (
- BaseCumulativeTransformOutputParser,
- BaseTransformOutputParser,
-)
-from langchain_core.output_parsers.xml import XMLOutputParser
+from importlib import import_module
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from langchain_core.output_parsers.base import (
+ BaseGenerationOutputParser,
+ BaseLLMOutputParser,
+ BaseOutputParser,
+ )
+ from langchain_core.output_parsers.json import (
+ JsonOutputParser,
+ SimpleJsonOutputParser,
+ )
+ from langchain_core.output_parsers.list import (
+ CommaSeparatedListOutputParser,
+ ListOutputParser,
+ MarkdownListOutputParser,
+ NumberedListOutputParser,
+ )
+ from langchain_core.output_parsers.openai_tools import (
+ JsonOutputKeyToolsParser,
+ JsonOutputToolsParser,
+ PydanticToolsParser,
+ )
+ from langchain_core.output_parsers.pydantic import PydanticOutputParser
+ from langchain_core.output_parsers.string import StrOutputParser
+ from langchain_core.output_parsers.transform import (
+ BaseCumulativeTransformOutputParser,
+ BaseTransformOutputParser,
+ )
+ from langchain_core.output_parsers.xml import XMLOutputParser
__all__ = [
"BaseLLMOutputParser",
@@ -57,3 +64,39 @@ __all__ = [
"JsonOutputKeyToolsParser",
"PydanticToolsParser",
]
+
+_dynamic_imports = {
+ "BaseLLMOutputParser": "base",
+ "BaseGenerationOutputParser": "base",
+ "BaseOutputParser": "base",
+ "JsonOutputParser": "json",
+ "SimpleJsonOutputParser": "json",
+ "ListOutputParser": "list",
+ "CommaSeparatedListOutputParser": "list",
+ "MarkdownListOutputParser": "list",
+ "NumberedListOutputParser": "list",
+ "JsonOutputKeyToolsParser": "openai_tools",
+ "JsonOutputToolsParser": "openai_tools",
+ "PydanticToolsParser": "openai_tools",
+ "PydanticOutputParser": "pydantic",
+ "StrOutputParser": "string",
+ "BaseTransformOutputParser": "transform",
+ "BaseCumulativeTransformOutputParser": "transform",
+ "XMLOutputParser": "xml",
+}
+
+
+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__)
diff --git a/libs/core/langchain_core/outputs/__init__.py b/libs/core/langchain_core/outputs/__init__.py
index f7b737eb1aa..07520833c9e 100644
--- a/libs/core/langchain_core/outputs/__init__.py
+++ b/libs/core/langchain_core/outputs/__init__.py
@@ -21,11 +21,18 @@ in the AIMessage object, it is recommended to access it from there rather than
from the `LLMResult` object.
"""
-from langchain_core.outputs.chat_generation import ChatGeneration, ChatGenerationChunk
-from langchain_core.outputs.chat_result import ChatResult
-from langchain_core.outputs.generation import Generation, GenerationChunk
-from langchain_core.outputs.llm_result import LLMResult
-from langchain_core.outputs.run_info import RunInfo
+from importlib import import_module
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from langchain_core.outputs.chat_generation import (
+ ChatGeneration,
+ ChatGenerationChunk,
+ )
+ from langchain_core.outputs.chat_result import ChatResult
+ from langchain_core.outputs.generation import Generation, GenerationChunk
+ from langchain_core.outputs.llm_result import LLMResult
+ from langchain_core.outputs.run_info import RunInfo
__all__ = [
"ChatGeneration",
@@ -36,3 +43,29 @@ __all__ = [
"LLMResult",
"RunInfo",
]
+
+_dynamic_imports = {
+ "ChatGeneration": "chat_generation",
+ "ChatGenerationChunk": "chat_generation",
+ "ChatResult": "chat_result",
+ "Generation": "generation",
+ "GenerationChunk": "generation",
+ "LLMResult": "llm_result",
+ "RunInfo": "run_info",
+}
+
+
+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__)
diff --git a/libs/core/langchain_core/prompts/__init__.py b/libs/core/langchain_core/prompts/__init__.py
index 4565018b6a3..16364182744 100644
--- a/libs/core/langchain_core/prompts/__init__.py
+++ b/libs/core/langchain_core/prompts/__init__.py
@@ -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__)
diff --git a/libs/core/langchain_core/runnables/__init__.py b/libs/core/langchain_core/runnables/__init__.py
index 44c95519c08..1c3a7f1b1a9 100644
--- a/libs/core/langchain_core/runnables/__init__.py
+++ b/libs/core/langchain_core/runnables/__init__.py
@@ -17,42 +17,46 @@ creating more responsive UX.
This module contains schema and implementation of LangChain Runnables primitives.
"""
-from langchain_core.runnables.base import (
- Runnable,
- RunnableBinding,
- RunnableGenerator,
- RunnableLambda,
- RunnableMap,
- RunnableParallel,
- RunnableSequence,
- RunnableSerializable,
- chain,
-)
-from langchain_core.runnables.branch import RunnableBranch
-from langchain_core.runnables.config import (
- RunnableConfig,
- ensure_config,
- get_config_list,
- patch_config,
- run_in_executor,
-)
-from langchain_core.runnables.fallbacks import RunnableWithFallbacks
-from langchain_core.runnables.history import RunnableWithMessageHistory
-from langchain_core.runnables.passthrough import (
- RunnableAssign,
- RunnablePassthrough,
- RunnablePick,
-)
-from langchain_core.runnables.router import RouterInput, RouterRunnable
-from langchain_core.runnables.utils import (
- AddableDict,
- ConfigurableField,
- ConfigurableFieldMultiOption,
- ConfigurableFieldSingleOption,
- ConfigurableFieldSpec,
- aadd,
- add,
-)
+from importlib import import_module
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from langchain_core.runnables.base import (
+ Runnable,
+ RunnableBinding,
+ RunnableGenerator,
+ RunnableLambda,
+ RunnableMap,
+ RunnableParallel,
+ RunnableSequence,
+ RunnableSerializable,
+ chain,
+ )
+ from langchain_core.runnables.branch import RunnableBranch
+ from langchain_core.runnables.config import (
+ RunnableConfig,
+ ensure_config,
+ get_config_list,
+ patch_config,
+ run_in_executor,
+ )
+ from langchain_core.runnables.fallbacks import RunnableWithFallbacks
+ from langchain_core.runnables.history import RunnableWithMessageHistory
+ from langchain_core.runnables.passthrough import (
+ RunnableAssign,
+ RunnablePassthrough,
+ RunnablePick,
+ )
+ from langchain_core.runnables.router import RouterInput, RouterRunnable
+ from langchain_core.runnables.utils import (
+ AddableDict,
+ ConfigurableField,
+ ConfigurableFieldMultiOption,
+ ConfigurableFieldSingleOption,
+ ConfigurableFieldSpec,
+ aadd,
+ add,
+ )
__all__ = [
"chain",
@@ -85,3 +89,51 @@ __all__ = [
"aadd",
"add",
]
+
+_dynamic_imports = {
+ "chain": "base",
+ "Runnable": "base",
+ "RunnableBinding": "base",
+ "RunnableGenerator": "base",
+ "RunnableLambda": "base",
+ "RunnableMap": "base",
+ "RunnableParallel": "base",
+ "RunnableSequence": "base",
+ "RunnableSerializable": "base",
+ "RunnableBranch": "branch",
+ "RunnableConfig": "config",
+ "ensure_config": "config",
+ "get_config_list": "config",
+ "patch_config": "config",
+ "run_in_executor": "config",
+ "RunnableWithFallbacks": "fallbacks",
+ "RunnableWithMessageHistory": "history",
+ "RunnableAssign": "passthrough",
+ "RunnablePassthrough": "passthrough",
+ "RunnablePick": "passthrough",
+ "RouterInput": "router",
+ "RouterRunnable": "router",
+ "AddableDict": "utils",
+ "ConfigurableField": "utils",
+ "ConfigurableFieldMultiOption": "utils",
+ "ConfigurableFieldSingleOption": "utils",
+ "ConfigurableFieldSpec": "utils",
+ "aadd": "utils",
+ "add": "utils",
+}
+
+
+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__)
diff --git a/libs/core/langchain_core/tools/__init__.py b/libs/core/langchain_core/tools/__init__.py
index ec521295e3f..f6189064872 100644
--- a/libs/core/langchain_core/tools/__init__.py
+++ b/libs/core/langchain_core/tools/__init__.py
@@ -19,33 +19,37 @@ tool for the job.
from __future__ import annotations
-from langchain_core.tools.base import (
- FILTERED_ARGS,
- ArgsSchema,
- BaseTool,
- BaseToolkit,
- InjectedToolArg,
- InjectedToolCallId,
- SchemaAnnotationError,
- ToolException,
- _get_runnable_config_param,
- create_schema_from_function,
-)
-from langchain_core.tools.convert import (
- convert_runnable_to_tool,
- tool,
-)
-from langchain_core.tools.render import (
- ToolsRenderer,
- render_text_description,
- render_text_description_and_args,
-)
-from langchain_core.tools.retriever import (
- RetrieverInput,
- create_retriever_tool,
-)
-from langchain_core.tools.simple import Tool
-from langchain_core.tools.structured import StructuredTool
+from importlib import import_module
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from langchain_core.tools.base import (
+ FILTERED_ARGS,
+ ArgsSchema,
+ BaseTool,
+ BaseToolkit,
+ InjectedToolArg,
+ InjectedToolCallId,
+ SchemaAnnotationError,
+ ToolException,
+ _get_runnable_config_param,
+ create_schema_from_function,
+ )
+ from langchain_core.tools.convert import (
+ convert_runnable_to_tool,
+ tool,
+ )
+ from langchain_core.tools.render import (
+ ToolsRenderer,
+ render_text_description,
+ render_text_description_and_args,
+ )
+ from langchain_core.tools.retriever import (
+ RetrieverInput,
+ create_retriever_tool,
+ )
+ from langchain_core.tools.simple import Tool
+ from langchain_core.tools.structured import StructuredTool
__all__ = [
"ArgsSchema",
@@ -68,3 +72,41 @@ __all__ = [
"Tool",
"StructuredTool",
]
+
+_dynamic_imports = {
+ "FILTERED_ARGS": "base",
+ "ArgsSchema": "base",
+ "BaseTool": "base",
+ "BaseToolkit": "base",
+ "InjectedToolArg": "base",
+ "InjectedToolCallId": "base",
+ "SchemaAnnotationError": "base",
+ "ToolException": "base",
+ "_get_runnable_config_param": "base",
+ "create_schema_from_function": "base",
+ "convert_runnable_to_tool": "convert",
+ "tool": "convert",
+ "ToolsRenderer": "render",
+ "render_text_description": "render",
+ "render_text_description_and_args": "render",
+ "RetrieverInput": "retriever",
+ "create_retriever_tool": "retriever",
+ "Tool": "simple",
+ "StructuredTool": "structured",
+}
+
+
+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__)
diff --git a/libs/core/langchain_core/tracers/__init__.py b/libs/core/langchain_core/tracers/__init__.py
index 05440d395a7..7d33cad5eb4 100644
--- a/libs/core/langchain_core/tracers/__init__.py
+++ b/libs/core/langchain_core/tracers/__init__.py
@@ -8,6 +8,21 @@
--> # Examples: LogStreamCallbackHandler
""" # noqa: E501
+from importlib import import_module
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from langchain_core.tracers.base import BaseTracer
+ from langchain_core.tracers.evaluation import EvaluatorCallbackHandler
+ from langchain_core.tracers.langchain import LangChainTracer
+ from langchain_core.tracers.log_stream import (
+ LogStreamCallbackHandler,
+ RunLog,
+ RunLogPatch,
+ )
+ from langchain_core.tracers.schemas import Run
+ from langchain_core.tracers.stdout import ConsoleCallbackHandler
+
__all__ = [
"BaseTracer",
"EvaluatorCallbackHandler",
@@ -19,13 +34,29 @@ __all__ = [
"LogStreamCallbackHandler",
]
-from langchain_core.tracers.base import BaseTracer
-from langchain_core.tracers.evaluation import EvaluatorCallbackHandler
-from langchain_core.tracers.langchain import LangChainTracer
-from langchain_core.tracers.log_stream import (
- LogStreamCallbackHandler,
- RunLog,
- RunLogPatch,
-)
-from langchain_core.tracers.schemas import Run
-from langchain_core.tracers.stdout import ConsoleCallbackHandler
+_dynamic_imports = {
+ "BaseTracer": "base",
+ "EvaluatorCallbackHandler": "evaluation",
+ "LangChainTracer": "langchain",
+ "LogStreamCallbackHandler": "log_stream",
+ "RunLog": "log_stream",
+ "RunLogPatch": "log_stream",
+ "Run": "schemas",
+ "ConsoleCallbackHandler": "stdout",
+}
+
+
+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__)
diff --git a/libs/core/langchain_core/utils/__init__.py b/libs/core/langchain_core/utils/__init__.py
index 1af695b4fb6..38c1467f794 100644
--- a/libs/core/langchain_core/utils/__init__.py
+++ b/libs/core/langchain_core/utils/__init__.py
@@ -3,32 +3,38 @@
These functions do not depend on any other LangChain module.
"""
-from langchain_core.utils import image
-from langchain_core.utils.aiter import abatch_iterate
-from langchain_core.utils.env import get_from_dict_or_env, get_from_env
-from langchain_core.utils.formatting import StrictFormatter, formatter
-from langchain_core.utils.input import (
- get_bolded_text,
- get_color_mapping,
- get_colored_text,
- print_text,
-)
-from langchain_core.utils.iter import batch_iterate
-from langchain_core.utils.loading import try_load_from_hub
-from langchain_core.utils.pydantic import pre_init
-from langchain_core.utils.strings import comma_list, stringify_dict, stringify_value
-from langchain_core.utils.utils import (
- build_extra_kwargs,
- check_package_version,
- convert_to_secret_str,
- from_env,
- get_pydantic_field_names,
- guard_import,
- mock_now,
- raise_for_status_with_text,
- secret_from_env,
- xor_args,
-)
+from importlib import import_module
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ # for type checking and IDE support, we include the imports here
+ # but we don't want to eagerly import them at runtime
+ from langchain_core.utils import image
+ from langchain_core.utils.aiter import abatch_iterate
+ from langchain_core.utils.env import get_from_dict_or_env, get_from_env
+ from langchain_core.utils.formatting import StrictFormatter, formatter
+ from langchain_core.utils.input import (
+ get_bolded_text,
+ get_color_mapping,
+ get_colored_text,
+ print_text,
+ )
+ from langchain_core.utils.iter import batch_iterate
+ from langchain_core.utils.loading import try_load_from_hub
+ from langchain_core.utils.pydantic import pre_init
+ from langchain_core.utils.strings import comma_list, stringify_dict, stringify_value
+ from langchain_core.utils.utils import (
+ build_extra_kwargs,
+ check_package_version,
+ convert_to_secret_str,
+ from_env,
+ get_pydantic_field_names,
+ guard_import,
+ mock_now,
+ raise_for_status_with_text,
+ secret_from_env,
+ xor_args,
+ )
__all__ = [
"build_extra_kwargs",
@@ -58,3 +64,48 @@ __all__ = [
"from_env",
"secret_from_env",
]
+
+_dynamic_imports = {
+ "image": "__module__",
+ "abatch_iterate": "aiter",
+ "get_from_dict_or_env": "env",
+ "get_from_env": "env",
+ "StrictFormatter": "formatting",
+ "formatter": "formatting",
+ "get_bolded_text": "input",
+ "get_color_mapping": "input",
+ "get_colored_text": "input",
+ "print_text": "input",
+ "batch_iterate": "iter",
+ "try_load_from_hub": "loading",
+ "pre_init": "pydantic",
+ "comma_list": "strings",
+ "stringify_dict": "strings",
+ "stringify_value": "strings",
+ "build_extra_kwargs": "utils",
+ "check_package_version": "utils",
+ "convert_to_secret_str": "utils",
+ "from_env": "utils",
+ "get_pydantic_field_names": "utils",
+ "guard_import": "utils",
+ "mock_now": "utils",
+ "secret_from_env": "utils",
+ "xor_args": "utils",
+ "raise_for_status_with_text": "utils",
+}
+
+
+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__)