Skip to content

Commit 80ebe03

Browse files
ogabrielluizautofix-ci[bot]edwinjosechittilappilly
authored
refactor(core): implement centralized dynamic lazy import system for components (langflow-ai#8932)
* feat: add import utilities for LangFlow components - Introduced a new module `_importing.py` containing the `import_mod` function. - This function dynamically imports attributes from specified modules, enhancing modularity and flexibility in component initialization. - Comprehensive docstring added for clarity on usage and parameters. * feat: implement dynamic imports for LangFlow components - Added dynamic import functionality to various LangFlow components, allowing for lazy loading of attributes on access. - Introduced mapping in each component's to manage imports efficiently. - Enhanced error handling for import failures, providing clearer messages for missing attributes. - Updated method to reflect available attributes for better introspection and tab-completion support. - Comprehensive docstrings added to improve documentation and usability. * test: add comprehensive tests for dynamic imports and component accessibility - Introduced integration tests for dynamic import functionality, ensuring components are discoverable and instantiable post-refactor. - Added unit tests for the `_import_utils` module, validating the `import_mod` function's behavior and error handling. - Implemented tests to confirm all component modules are importable and maintain backward compatibility with existing import patterns. - Enhanced performance tests to measure lazy loading efficiency and memory usage during component access. - Ensured that all components have the required attributes for dynamic loading and that circular imports are prevented. * chore: update ruff pre-commit hook to version 0.12.2 in configuration file * refactor: update warning handling for dynamic imports - Moved the warning suppression for LangChainDeprecationWarning into the dynamic import context to ensure it only applies during the import process. - This change enhances clarity and maintains the original functionality while improving the robustness of the import mechanism. * test: enhance dynamic import integration tests for component attributes - Removed unnecessary import of AgentComponent and added assertions to verify essential attributes of OpenAIModelComponent, including display_name, description, icon, and inputs. - Ensured that each input field has the required attributes for better validation of component integrity during dynamic imports. * refactor: update import paths for Message class in conversation utilities - Changed the import of the Message class from langflow.field_typing to langflow.schema.message across multiple utility files, ensuring consistency and alignment with the updated module structure. - This refactor enhances code clarity and maintains compatibility with the latest schema definitions. * refactor: remove Vectara components from LangFlow - Deleted the Vectara components module from the codebase, streamlining the component structure. - This change helps to reduce complexity and maintain focus on core functionalities. * refactor: remove Vectara references from LangFlow component imports - Eliminated Vectara from both the import statements and dynamic imports mapping, streamlining the component structure. - This change contributes to reducing complexity and maintaining focus on core functionalities within the LangFlow framework. * [autofix.ci] apply automated fixes * fix: remove 'vectara' from __all__ in components module * refactor: improve error handling tests for dynamic imports * test: add tests for ModuleNotFoundError handling with None and special module names --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Edwin Jose <[email protected]>
1 parent 087c1a2 commit 80ebe03

File tree

58 files changed

+3174
-232
lines changed

Some content is hidden

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

58 files changed

+3174
-232
lines changed
Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
"""LangFlow Components module."""
2+
3+
from __future__ import annotations
4+
5+
from typing import TYPE_CHECKING, Any
6+
7+
from langflow.components._importing import import_mod
8+
9+
if TYPE_CHECKING:
10+
from langflow.components import (
11+
Notion,
12+
agentql,
13+
agents,
14+
aiml,
15+
amazon,
16+
anthropic,
17+
apify,
18+
arxiv,
19+
assemblyai,
20+
azure,
21+
baidu,
22+
bing,
23+
cleanlab,
24+
cloudflare,
25+
cohere,
26+
composio,
27+
confluence,
28+
crewai,
29+
custom_component,
30+
data,
31+
datastax,
32+
deepseek,
33+
docling,
34+
duckduckgo,
35+
embeddings,
36+
exa,
37+
firecrawl,
38+
git,
39+
glean,
40+
google,
41+
groq,
42+
helpers,
43+
homeassistant,
44+
huggingface,
45+
ibm,
46+
icosacomputing,
47+
input_output,
48+
langchain_utilities,
49+
langwatch,
50+
lmstudio,
51+
logic,
52+
maritalk,
53+
mem0,
54+
mistral,
55+
models,
56+
needle,
57+
notdiamond,
58+
novita,
59+
nvidia,
60+
olivya,
61+
ollama,
62+
openai,
63+
openrouter,
64+
perplexity,
65+
processing,
66+
prototypes,
67+
redis,
68+
sambanova,
69+
scrapegraph,
70+
searchapi,
71+
serpapi,
72+
tavily,
73+
tools,
74+
twelvelabs,
75+
unstructured,
76+
vectorstores,
77+
vertexai,
78+
wikipedia,
79+
wolframalpha,
80+
xai,
81+
yahoosearch,
82+
youtube,
83+
zep,
84+
)
85+
86+
_dynamic_imports = {
87+
"agents": "langflow.components.agents",
88+
"data": "langflow.components.data",
89+
"processing": "langflow.components.processing",
90+
"vectorstores": "langflow.components.vectorstores",
91+
"tools": "langflow.components.tools",
92+
"models": "langflow.components.models",
93+
"embeddings": "langflow.components.embeddings",
94+
"helpers": "langflow.components.helpers",
95+
"input_output": "langflow.components.input_output",
96+
"logic": "langflow.components.logic",
97+
"custom_component": "langflow.components.custom_component",
98+
"prototypes": "langflow.components.prototypes",
99+
"openai": "langflow.components.openai",
100+
"anthropic": "langflow.components.anthropic",
101+
"google": "langflow.components.google",
102+
"azure": "langflow.components.azure",
103+
"huggingface": "langflow.components.huggingface",
104+
"ollama": "langflow.components.ollama",
105+
"groq": "langflow.components.groq",
106+
"cohere": "langflow.components.cohere",
107+
"mistral": "langflow.components.mistral",
108+
"deepseek": "langflow.components.deepseek",
109+
"nvidia": "langflow.components.nvidia",
110+
"amazon": "langflow.components.amazon",
111+
"vertexai": "langflow.components.vertexai",
112+
"xai": "langflow.components.xai",
113+
"perplexity": "langflow.components.perplexity",
114+
"openrouter": "langflow.components.openrouter",
115+
"lmstudio": "langflow.components.lmstudio",
116+
"sambanova": "langflow.components.sambanova",
117+
"maritalk": "langflow.components.maritalk",
118+
"novita": "langflow.components.novita",
119+
"olivya": "langflow.components.olivya",
120+
"notdiamond": "langflow.components.notdiamond",
121+
"needle": "langflow.components.needle",
122+
"cloudflare": "langflow.components.cloudflare",
123+
"baidu": "langflow.components.baidu",
124+
"aiml": "langflow.components.aiml",
125+
"ibm": "langflow.components.ibm",
126+
"langchain_utilities": "langflow.components.langchain_utilities",
127+
"crewai": "langflow.components.crewai",
128+
"composio": "langflow.components.composio",
129+
"mem0": "langflow.components.mem0",
130+
"datastax": "langflow.components.datastax",
131+
"cleanlab": "langflow.components.cleanlab",
132+
"langwatch": "langflow.components.langwatch",
133+
"icosacomputing": "langflow.components.icosacomputing",
134+
"homeassistant": "langflow.components.homeassistant",
135+
"agentql": "langflow.components.agentql",
136+
"assemblyai": "langflow.components.assemblyai",
137+
"twelvelabs": "langflow.components.twelvelabs",
138+
"docling": "langflow.components.docling",
139+
"unstructured": "langflow.components.unstructured",
140+
"redis": "langflow.components.redis",
141+
"zep": "langflow.components.zep",
142+
"bing": "langflow.components.bing",
143+
"duckduckgo": "langflow.components.duckduckgo",
144+
"serpapi": "langflow.components.serpapi",
145+
"searchapi": "langflow.components.searchapi",
146+
"tavily": "langflow.components.tavily",
147+
"exa": "langflow.components.exa",
148+
"glean": "langflow.components.glean",
149+
"yahoosearch": "langflow.components.yahoosearch",
150+
"apify": "langflow.components.apify",
151+
"arxiv": "langflow.components.arxiv",
152+
"confluence": "langflow.components.confluence",
153+
"firecrawl": "langflow.components.firecrawl",
154+
"git": "langflow.components.git",
155+
"wikipedia": "langflow.components.wikipedia",
156+
"youtube": "langflow.components.youtube",
157+
"scrapegraph": "langflow.components.scrapegraph",
158+
"Notion": "langflow.components.Notion",
159+
"wolframalpha": "langflow.components.wolframalpha",
160+
}
161+
162+
__all__: list[str] = [
163+
"Notion",
164+
"agentql",
165+
"agents",
166+
"aiml",
167+
"amazon",
168+
"anthropic",
169+
"apify",
170+
"arxiv",
171+
"assemblyai",
172+
"azure",
173+
"baidu",
174+
"bing",
175+
"cleanlab",
176+
"cloudflare",
177+
"cohere",
178+
"composio",
179+
"confluence",
180+
"crewai",
181+
"custom_component",
182+
"data",
183+
"datastax",
184+
"deepseek",
185+
"docling",
186+
"duckduckgo",
187+
"embeddings",
188+
"exa",
189+
"firecrawl",
190+
"git",
191+
"glean",
192+
"google",
193+
"groq",
194+
"helpers",
195+
"homeassistant",
196+
"huggingface",
197+
"ibm",
198+
"icosacomputing",
199+
"input_output",
200+
"langchain_utilities",
201+
"langwatch",
202+
"lmstudio",
203+
"logic",
204+
"maritalk",
205+
"mem0",
206+
"mistral",
207+
"models",
208+
"needle",
209+
"notdiamond",
210+
"novita",
211+
"nvidia",
212+
"olivya",
213+
"ollama",
214+
"openai",
215+
"openrouter",
216+
"perplexity",
217+
"processing",
218+
"prototypes",
219+
"redis",
220+
"sambanova",
221+
"scrapegraph",
222+
"searchapi",
223+
"serpapi",
224+
"tavily",
225+
"tools",
226+
"twelvelabs",
227+
"unstructured",
228+
"vectorstores",
229+
"vertexai",
230+
"wikipedia",
231+
"wolframalpha",
232+
"xai",
233+
"yahoosearch",
234+
"youtube",
235+
"zep",
236+
]
237+
238+
239+
def __getattr__(attr_name: str) -> Any:
240+
"""Lazily import component modules on attribute access.
241+
242+
Args:
243+
attr_name (str): The attribute/module name to import.
244+
245+
Returns:
246+
Any: The imported module or attribute.
247+
248+
Raises:
249+
AttributeError: If the attribute is not a known component or cannot be imported.
250+
"""
251+
if attr_name not in _dynamic_imports:
252+
msg = f"module '{__name__}' has no attribute '{attr_name}'"
253+
raise AttributeError(msg)
254+
try:
255+
# Use import_mod as in LangChain, passing the module name and package
256+
result = import_mod(attr_name, "__module__", __spec__.parent)
257+
except (ModuleNotFoundError, ImportError, AttributeError) as e:
258+
msg = f"Could not import '{attr_name}' from '{__name__}': {e}"
259+
raise AttributeError(msg) from e
260+
globals()[attr_name] = result # Cache for future access
261+
return result
262+
263+
264+
def __dir__() -> list[str]:
265+
"""Return list of available attributes for tab-completion and dir()."""
266+
return list(__all__)
267+
268+
269+
# Optional: Consistency check (can be removed in production)
270+
_missing = set(__all__) - set(_dynamic_imports)
271+
if _missing:
272+
msg = f"Missing dynamic import mapping for: {', '.join(_missing)}"
273+
raise ImportError(msg)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""Import utilities for LangFlow components."""
2+
3+
from __future__ import annotations
4+
5+
from importlib import import_module
6+
7+
8+
def import_mod(
9+
attr_name: str,
10+
module_name: str | None,
11+
package: str | None,
12+
) -> object:
13+
"""Import an attribute from a module located in a package.
14+
15+
This utility function is used in custom __getattr__ methods within __init__.py
16+
files to dynamically import attributes.
17+
18+
Args:
19+
attr_name: The name of the attribute to import.
20+
module_name: The name of the module to import from. If None, the attribute
21+
is imported from the package itself.
22+
package: The name of the package where the module is located.
23+
"""
24+
if module_name == "__module__" or module_name is None:
25+
try:
26+
result = import_module(f".{attr_name}", package=package)
27+
except ModuleNotFoundError:
28+
msg = f"module '{package!r}' has no attribute {attr_name!r}"
29+
raise AttributeError(msg) from None
30+
else:
31+
try:
32+
module = import_module(f".{module_name}", package=package)
33+
except ModuleNotFoundError:
34+
msg = f"module '{package!r}.{module_name!r}' not found"
35+
raise ImportError(msg) from None
36+
result = getattr(module, attr_name)
37+
return result
Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,37 @@
1-
from .aiml import AIMLModelComponent
2-
from .aiml_embeddings import AIMLEmbeddingsComponent
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING, Any
4+
5+
from langflow.components._importing import import_mod
6+
7+
if TYPE_CHECKING:
8+
from langflow.components.aiml.aiml import AIMLModelComponent
9+
from langflow.components.aiml.aiml_embeddings import AIMLEmbeddingsComponent
10+
11+
_dynamic_imports = {
12+
"AIMLModelComponent": "aiml",
13+
"AIMLEmbeddingsComponent": "aiml_embeddings",
14+
}
315

416
__all__ = [
517
"AIMLEmbeddingsComponent",
618
"AIMLModelComponent",
719
]
20+
21+
22+
def __getattr__(attr_name: str) -> Any:
23+
"""Lazily import aiml components on attribute access."""
24+
if attr_name not in _dynamic_imports:
25+
msg = f"module '{__name__}' has no attribute '{attr_name}'"
26+
raise AttributeError(msg)
27+
try:
28+
result = import_mod(attr_name, _dynamic_imports[attr_name], __spec__.parent)
29+
except (ModuleNotFoundError, ImportError, AttributeError) as e:
30+
msg = f"Could not import '{attr_name}' from '{__name__}': {e}"
31+
raise AttributeError(msg) from e
32+
globals()[attr_name] = result
33+
return result
34+
35+
36+
def __dir__() -> list[str]:
37+
return list(__all__)
Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,36 @@
1-
from .amazon_bedrock_embedding import AmazonBedrockEmbeddingsComponent
2-
from .amazon_bedrock_model import AmazonBedrockComponent
3-
from .s3_bucket_uploader import S3BucketUploaderComponent
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING, Any
4+
5+
from langflow.components._importing import import_mod
6+
7+
if TYPE_CHECKING:
8+
from langflow.components.amazon.amazon_bedrock_embedding import AmazonBedrockEmbeddingsComponent
9+
from langflow.components.amazon.amazon_bedrock_model import AmazonBedrockComponent
10+
from langflow.components.amazon.s3_bucket_uploader import S3BucketUploaderComponent
11+
12+
_dynamic_imports = {
13+
"AmazonBedrockEmbeddingsComponent": "amazon_bedrock_embedding",
14+
"AmazonBedrockComponent": "amazon_bedrock_model",
15+
"S3BucketUploaderComponent": "s3_bucket_uploader",
16+
}
417

518
__all__ = ["AmazonBedrockComponent", "AmazonBedrockEmbeddingsComponent", "S3BucketUploaderComponent"]
19+
20+
21+
def __getattr__(attr_name: str) -> Any:
22+
"""Lazily import amazon components on attribute access."""
23+
if attr_name not in _dynamic_imports:
24+
msg = f"module '{__name__}' has no attribute '{attr_name}'"
25+
raise AttributeError(msg)
26+
try:
27+
result = import_mod(attr_name, _dynamic_imports[attr_name], __spec__.parent)
28+
except (ModuleNotFoundError, ImportError, AttributeError) as e:
29+
msg = f"Could not import '{attr_name}' from '{__name__}': {e}"
30+
raise AttributeError(msg) from e
31+
globals()[attr_name] = result
32+
return result
33+
34+
35+
def __dir__() -> list[str]:
36+
return list(__all__)

0 commit comments

Comments
 (0)