lfx-nightly 0.2.0.dev41__py3-none-any.whl → 0.3.0.dev3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lfx/__main__.py +137 -6
- lfx/_assets/component_index.json +1 -1
- lfx/base/agents/agent.py +10 -6
- lfx/base/agents/altk_base_agent.py +5 -3
- lfx/base/agents/altk_tool_wrappers.py +1 -1
- lfx/base/agents/events.py +1 -1
- lfx/base/agents/utils.py +4 -0
- lfx/base/composio/composio_base.py +78 -41
- lfx/base/data/cloud_storage_utils.py +156 -0
- lfx/base/data/docling_utils.py +130 -55
- lfx/base/datastax/astradb_base.py +75 -64
- lfx/base/embeddings/embeddings_class.py +113 -0
- lfx/base/models/__init__.py +11 -1
- lfx/base/models/google_generative_ai_constants.py +33 -9
- lfx/base/models/model_metadata.py +6 -0
- lfx/base/models/ollama_constants.py +196 -30
- lfx/base/models/openai_constants.py +37 -10
- lfx/base/models/unified_models.py +1123 -0
- lfx/base/models/watsonx_constants.py +43 -4
- lfx/base/prompts/api_utils.py +40 -5
- lfx/base/tools/component_tool.py +2 -9
- lfx/cli/__init__.py +10 -2
- lfx/cli/commands.py +3 -0
- lfx/cli/run.py +65 -409
- lfx/cli/script_loader.py +18 -7
- lfx/cli/validation.py +6 -3
- lfx/components/__init__.py +0 -3
- lfx/components/composio/github_composio.py +1 -1
- lfx/components/cuga/cuga_agent.py +39 -27
- lfx/components/data_source/api_request.py +4 -2
- lfx/components/datastax/astradb_assistant_manager.py +4 -2
- lfx/components/docling/__init__.py +45 -11
- lfx/components/docling/docling_inline.py +39 -49
- lfx/components/docling/docling_remote.py +1 -0
- lfx/components/elastic/opensearch_multimodal.py +1733 -0
- lfx/components/files_and_knowledge/file.py +384 -36
- lfx/components/files_and_knowledge/ingestion.py +8 -0
- lfx/components/files_and_knowledge/retrieval.py +10 -0
- lfx/components/files_and_knowledge/save_file.py +91 -88
- lfx/components/langchain_utilities/ibm_granite_handler.py +211 -0
- lfx/components/langchain_utilities/tool_calling.py +37 -6
- lfx/components/llm_operations/batch_run.py +64 -18
- lfx/components/llm_operations/lambda_filter.py +213 -101
- lfx/components/llm_operations/llm_conditional_router.py +39 -7
- lfx/components/llm_operations/structured_output.py +38 -12
- lfx/components/models/__init__.py +16 -74
- lfx/components/models_and_agents/agent.py +51 -203
- lfx/components/models_and_agents/embedding_model.py +171 -255
- lfx/components/models_and_agents/language_model.py +54 -318
- lfx/components/models_and_agents/mcp_component.py +96 -10
- lfx/components/models_and_agents/prompt.py +105 -18
- lfx/components/ollama/ollama_embeddings.py +111 -29
- lfx/components/openai/openai_chat_model.py +1 -1
- lfx/components/processing/text_operations.py +580 -0
- lfx/components/vllm/__init__.py +37 -0
- lfx/components/vllm/vllm.py +141 -0
- lfx/components/vllm/vllm_embeddings.py +110 -0
- lfx/custom/custom_component/component.py +65 -10
- lfx/custom/custom_component/custom_component.py +8 -6
- lfx/events/observability/__init__.py +0 -0
- lfx/events/observability/lifecycle_events.py +111 -0
- lfx/field_typing/__init__.py +57 -58
- lfx/graph/graph/base.py +40 -1
- lfx/graph/utils.py +109 -30
- lfx/graph/vertex/base.py +75 -23
- lfx/graph/vertex/vertex_types.py +0 -5
- lfx/inputs/__init__.py +2 -0
- lfx/inputs/input_mixin.py +55 -0
- lfx/inputs/inputs.py +120 -0
- lfx/interface/components.py +24 -7
- lfx/interface/initialize/loading.py +42 -12
- lfx/io/__init__.py +2 -0
- lfx/run/__init__.py +5 -0
- lfx/run/base.py +464 -0
- lfx/schema/__init__.py +50 -0
- lfx/schema/data.py +1 -1
- lfx/schema/image.py +26 -7
- lfx/schema/message.py +104 -11
- lfx/schema/workflow.py +171 -0
- lfx/services/deps.py +12 -0
- lfx/services/interfaces.py +43 -1
- lfx/services/mcp_composer/service.py +7 -1
- lfx/services/schema.py +1 -0
- lfx/services/settings/auth.py +95 -4
- lfx/services/settings/base.py +11 -1
- lfx/services/settings/constants.py +2 -0
- lfx/services/settings/utils.py +82 -0
- lfx/services/storage/local.py +13 -8
- lfx/services/transaction/__init__.py +5 -0
- lfx/services/transaction/service.py +35 -0
- lfx/tests/unit/components/__init__.py +0 -0
- lfx/utils/constants.py +2 -0
- lfx/utils/mustache_security.py +79 -0
- lfx/utils/validate_cloud.py +81 -3
- {lfx_nightly-0.2.0.dev41.dist-info → lfx_nightly-0.3.0.dev3.dist-info}/METADATA +7 -2
- {lfx_nightly-0.2.0.dev41.dist-info → lfx_nightly-0.3.0.dev3.dist-info}/RECORD +98 -80
- {lfx_nightly-0.2.0.dev41.dist-info → lfx_nightly-0.3.0.dev3.dist-info}/WHEEL +0 -0
- {lfx_nightly-0.2.0.dev41.dist-info → lfx_nightly-0.3.0.dev3.dist-info}/entry_points.txt +0 -0
lfx/services/settings/utils.py
CHANGED
|
@@ -1,9 +1,74 @@
|
|
|
1
1
|
import platform
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
|
|
4
|
+
from cryptography.hazmat.primitives import serialization
|
|
5
|
+
from cryptography.hazmat.primitives.asymmetric import rsa
|
|
6
|
+
from cryptography.hazmat.primitives.serialization import load_pem_private_key
|
|
7
|
+
|
|
4
8
|
from lfx.log.logger import logger
|
|
5
9
|
|
|
6
10
|
|
|
11
|
+
class RSAKeyError(Exception):
|
|
12
|
+
"""Exception raised when RSA key operations fail."""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def derive_public_key_from_private(private_key_pem: str) -> str:
|
|
16
|
+
"""Derive a public key from a private key PEM string.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
private_key_pem: The private key in PEM format.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
str: The public key in PEM format.
|
|
23
|
+
|
|
24
|
+
Raises:
|
|
25
|
+
RSAKeyError: If the private key is invalid or cannot be processed.
|
|
26
|
+
"""
|
|
27
|
+
try:
|
|
28
|
+
private_key = load_pem_private_key(private_key_pem.encode(), password=None)
|
|
29
|
+
return (
|
|
30
|
+
private_key.public_key()
|
|
31
|
+
.public_bytes(
|
|
32
|
+
encoding=serialization.Encoding.PEM,
|
|
33
|
+
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
|
34
|
+
)
|
|
35
|
+
.decode("utf-8")
|
|
36
|
+
)
|
|
37
|
+
except Exception as e:
|
|
38
|
+
msg = f"Failed to derive public key from private key: {e}"
|
|
39
|
+
logger.error(msg)
|
|
40
|
+
raise RSAKeyError(msg) from e
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def generate_rsa_key_pair() -> tuple[str, str]:
|
|
44
|
+
"""Generate an RSA key pair for RS256 JWT signing.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
tuple[str, str]: A tuple of (private_key_pem, public_key_pem) as strings.
|
|
48
|
+
"""
|
|
49
|
+
private_key = rsa.generate_private_key(
|
|
50
|
+
public_exponent=65537,
|
|
51
|
+
key_size=2048,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
private_key_pem = private_key.private_bytes(
|
|
55
|
+
encoding=serialization.Encoding.PEM,
|
|
56
|
+
format=serialization.PrivateFormat.PKCS8,
|
|
57
|
+
encryption_algorithm=serialization.NoEncryption(),
|
|
58
|
+
).decode("utf-8")
|
|
59
|
+
|
|
60
|
+
public_key_pem = (
|
|
61
|
+
private_key.public_key()
|
|
62
|
+
.public_bytes(
|
|
63
|
+
encoding=serialization.Encoding.PEM,
|
|
64
|
+
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
|
65
|
+
)
|
|
66
|
+
.decode("utf-8")
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
return private_key_pem, public_key_pem
|
|
70
|
+
|
|
71
|
+
|
|
7
72
|
def set_secure_permissions(file_path: Path) -> None:
|
|
8
73
|
if platform.system() in {"Linux", "Darwin"}: # Unix/Linux/Mac
|
|
9
74
|
file_path.chmod(0o600)
|
|
@@ -38,3 +103,20 @@ def write_secret_to_file(path: Path, value: str) -> None:
|
|
|
38
103
|
|
|
39
104
|
def read_secret_from_file(path: Path) -> str:
|
|
40
105
|
return path.read_text(encoding="utf-8")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def write_public_key_to_file(path: Path, value: str) -> None:
|
|
109
|
+
"""Write a public key to file with appropriate permissions (0o644).
|
|
110
|
+
|
|
111
|
+
Public keys can be readable by others but should only be writable by owner.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
path: The file path to write to.
|
|
115
|
+
value: The public key content.
|
|
116
|
+
"""
|
|
117
|
+
path.write_text(value, encoding="utf-8")
|
|
118
|
+
try:
|
|
119
|
+
if platform.system() in {"Linux", "Darwin"}:
|
|
120
|
+
path.chmod(0o644)
|
|
121
|
+
except Exception: # noqa: BLE001
|
|
122
|
+
logger.exception("Failed to set permissions on public key file")
|
lfx/services/storage/local.py
CHANGED
|
@@ -58,11 +58,12 @@ class LocalStorageService(StorageService):
|
|
|
58
58
|
return str(self.data_dir / flow_id / file_name)
|
|
59
59
|
|
|
60
60
|
def parse_file_path(self, full_path: str) -> tuple[str, str]:
|
|
61
|
-
"""Parse a full local storage path to extract flow_id and file_name.
|
|
61
|
+
r"""Parse a full local storage path to extract flow_id and file_name.
|
|
62
62
|
|
|
63
63
|
Args:
|
|
64
64
|
full_path: Filesystem path, may or may not include data_dir
|
|
65
|
-
e.g., "/data/user_123/image.png" or "user_123/image.png"
|
|
65
|
+
e.g., "/data/user_123/image.png" or "user_123/image.png". On Windows the
|
|
66
|
+
separators may be backslashes ("\\"). This method handles both.
|
|
66
67
|
|
|
67
68
|
Returns:
|
|
68
69
|
tuple[str, str]: A tuple of (flow_id, file_name)
|
|
@@ -78,15 +79,19 @@ class LocalStorageService(StorageService):
|
|
|
78
79
|
# Remove data_dir if present (but don't require it)
|
|
79
80
|
path_without_prefix = full_path
|
|
80
81
|
if full_path.startswith(data_dir_str):
|
|
81
|
-
|
|
82
|
+
# Strip both POSIX and Windows separators
|
|
83
|
+
path_without_prefix = full_path[len(data_dir_str) :].lstrip("/").lstrip("\\")
|
|
82
84
|
|
|
83
|
-
#
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
# Normalize separators so downstream logic is platform-agnostic
|
|
86
|
+
normalized_path = path_without_prefix.replace("\\", "/")
|
|
87
|
+
|
|
88
|
+
# Split from the right to get the filename; everything before the last
|
|
89
|
+
# "/" is the flow_id
|
|
90
|
+
if "/" not in normalized_path:
|
|
91
|
+
return "", normalized_path
|
|
87
92
|
|
|
88
93
|
# Use rsplit to split from the right, limiting to 1 split
|
|
89
|
-
flow_id, file_name =
|
|
94
|
+
flow_id, file_name = normalized_path.rsplit("/", 1)
|
|
90
95
|
return flow_id, file_name
|
|
91
96
|
|
|
92
97
|
async def save_file(self, flow_id: str, file_name: str, data: bytes, *, append: bool = False) -> None:
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Transaction service implementations for lfx."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from lfx.services.interfaces import TransactionServiceProtocol
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class NoopTransactionService(TransactionServiceProtocol):
|
|
11
|
+
"""No-operation transaction service for standalone lfx mode.
|
|
12
|
+
|
|
13
|
+
This service is used when lfx runs without a concrete transaction
|
|
14
|
+
service implementation (e.g., without langflow). All operations
|
|
15
|
+
are no-ops and transaction logging is disabled.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
async def log_transaction(
|
|
19
|
+
self,
|
|
20
|
+
flow_id: str,
|
|
21
|
+
vertex_id: str,
|
|
22
|
+
inputs: dict[str, Any] | None,
|
|
23
|
+
outputs: dict[str, Any] | None,
|
|
24
|
+
status: str,
|
|
25
|
+
target_id: str | None = None,
|
|
26
|
+
error: str | None = None,
|
|
27
|
+
) -> None:
|
|
28
|
+
"""No-op implementation of transaction logging.
|
|
29
|
+
|
|
30
|
+
In standalone mode, transactions are not persisted.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def is_enabled(self) -> bool:
|
|
34
|
+
"""Transaction logging is disabled in noop mode."""
|
|
35
|
+
return False
|
|
File without changes
|
lfx/utils/constants.py
CHANGED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Security utilities for mustache template processing."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
# Regex pattern for simple variables only - same as frontend
|
|
7
|
+
SIMPLE_VARIABLE_PATTERN = re.compile(r"\{\{([a-zA-Z_][a-zA-Z0-9_]*)\}\}")
|
|
8
|
+
|
|
9
|
+
# Patterns for complex mustache syntax that we want to block
|
|
10
|
+
DANGEROUS_PATTERNS = [
|
|
11
|
+
re.compile(r"\{\{\{"), # Triple braces (unescaped HTML in Mustache)
|
|
12
|
+
re.compile(r"\{\{#"), # Conditionals/sections start
|
|
13
|
+
re.compile(r"\{\{/"), # Conditionals/sections end
|
|
14
|
+
re.compile(r"\{\{\^"), # Inverted sections
|
|
15
|
+
re.compile(r"\{\{&"), # Unescaped variables
|
|
16
|
+
re.compile(r"\{\{>"), # Partials
|
|
17
|
+
re.compile(r"\{\{!"), # Comments
|
|
18
|
+
re.compile(r"\{\{\."), # Current context
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def validate_mustache_template(template: str) -> None:
|
|
23
|
+
"""Validate that a mustache template only contains simple variable substitutions.
|
|
24
|
+
|
|
25
|
+
Raises ValueError if complex mustache syntax is detected.
|
|
26
|
+
"""
|
|
27
|
+
if not template:
|
|
28
|
+
return
|
|
29
|
+
|
|
30
|
+
# Check for dangerous patterns
|
|
31
|
+
for pattern in DANGEROUS_PATTERNS:
|
|
32
|
+
if pattern.search(template):
|
|
33
|
+
msg = (
|
|
34
|
+
"Complex mustache syntax is not allowed. Only simple variable substitution "
|
|
35
|
+
"like {{variable}} is permitted."
|
|
36
|
+
)
|
|
37
|
+
raise ValueError(msg)
|
|
38
|
+
|
|
39
|
+
# Check that all {{ }} patterns are simple variables
|
|
40
|
+
all_mustache_patterns = re.findall(r"\{\{[^}]*\}\}", template)
|
|
41
|
+
for pattern in all_mustache_patterns:
|
|
42
|
+
if not SIMPLE_VARIABLE_PATTERN.match(pattern):
|
|
43
|
+
msg = f"Invalid mustache variable: {pattern}. Only simple variable names like {{{{variable}}}} are allowed."
|
|
44
|
+
raise ValueError(msg)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def safe_mustache_render(template: str, variables: dict[str, Any]) -> str:
|
|
48
|
+
"""Safely render a mustache template with only simple variable substitution.
|
|
49
|
+
|
|
50
|
+
This function performs a single-pass replacement of all {{variable}} patterns.
|
|
51
|
+
Variable values that themselves contain mustache-like patterns (e.g., "{{other}}")
|
|
52
|
+
will NOT be processed - they are treated as literal strings. This prevents
|
|
53
|
+
injection attacks where user-controlled values could introduce new template variables.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
template: The mustache template string
|
|
57
|
+
variables: Dictionary of variables to substitute
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
The rendered template
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
ValueError: If template contains complex mustache syntax
|
|
64
|
+
"""
|
|
65
|
+
# Validate template first
|
|
66
|
+
validate_mustache_template(template)
|
|
67
|
+
|
|
68
|
+
# Simple replacement - find all simple variables and replace them
|
|
69
|
+
def replace_variable(match):
|
|
70
|
+
var_name = match.group(1)
|
|
71
|
+
|
|
72
|
+
# Get the variable value directly (no dot notation support)
|
|
73
|
+
value = variables.get(var_name, "")
|
|
74
|
+
|
|
75
|
+
# Convert to string
|
|
76
|
+
return str(value) if value is not None else ""
|
|
77
|
+
|
|
78
|
+
# Replace all simple variables
|
|
79
|
+
return SIMPLE_VARIABLE_PATTERN.sub(replace_variable, template)
|
lfx/utils/validate_cloud.py
CHANGED
|
@@ -5,6 +5,20 @@ such as disabling certain features when running in Astra cloud environment.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import os
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def is_astra_cloud_environment() -> bool:
|
|
12
|
+
"""Check if we're running in an Astra cloud environment.
|
|
13
|
+
|
|
14
|
+
Check if the environment variable ASTRA_CLOUD_DISABLE_COMPONENT is set to true.
|
|
15
|
+
IF it is, then we know we are in an Astra cloud environment.
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
bool: True if running in an Astra cloud environment, False otherwise.
|
|
19
|
+
"""
|
|
20
|
+
disable_component = os.getenv("ASTRA_CLOUD_DISABLE_COMPONENT", "false")
|
|
21
|
+
return disable_component.lower().strip() == "true"
|
|
8
22
|
|
|
9
23
|
|
|
10
24
|
def raise_error_if_astra_cloud_disable_component(msg: str):
|
|
@@ -20,7 +34,71 @@ def raise_error_if_astra_cloud_disable_component(msg: str):
|
|
|
20
34
|
Raises:
|
|
21
35
|
ValueError: If running in an Astra cloud environment.
|
|
22
36
|
"""
|
|
23
|
-
if (
|
|
24
|
-
disable_component := os.getenv("ASTRA_CLOUD_DISABLE_COMPONENT", "false")
|
|
25
|
-
) and disable_component.lower().strip() == "true":
|
|
37
|
+
if is_astra_cloud_environment():
|
|
26
38
|
raise ValueError(msg)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# Mapping of component types to their disabled module names and component names when in Astra cloud environment.
|
|
42
|
+
# Keys are component type names (e.g., "docling")
|
|
43
|
+
# Values are sets containing both module filenames (e.g., "chunk_docling_document")
|
|
44
|
+
# and component names (e.g., "ChunkDoclingDocument")
|
|
45
|
+
# To add new disabled components in the future, simply add entries to this dictionary.
|
|
46
|
+
ASTRA_CLOUD_DISABLED_COMPONENTS: dict[str, set[str]] = {
|
|
47
|
+
"docling": {
|
|
48
|
+
# Module filenames (for dynamic loading)
|
|
49
|
+
"chunk_docling_document",
|
|
50
|
+
"docling_inline",
|
|
51
|
+
"export_docling_document",
|
|
52
|
+
# Component names (for index/cache loading)
|
|
53
|
+
"ChunkDoclingDocument",
|
|
54
|
+
"DoclingInline",
|
|
55
|
+
"ExportDoclingDocument",
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def is_component_disabled_in_astra_cloud(component_type: str, module_filename: str) -> bool:
|
|
61
|
+
"""Check if a specific component module should be disabled in cloud environment.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
component_type: The top-level component type (e.g., "docling")
|
|
65
|
+
module_filename: The module filename without extension (e.g., "chunk_docling_document")
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
bool: True if the component should be disabled, False otherwise.
|
|
69
|
+
"""
|
|
70
|
+
if not is_astra_cloud_environment():
|
|
71
|
+
return False
|
|
72
|
+
|
|
73
|
+
disabled_modules = ASTRA_CLOUD_DISABLED_COMPONENTS.get(component_type.lower(), set())
|
|
74
|
+
return module_filename in disabled_modules
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def filter_disabled_components_from_dict(modules_dict: dict[str, dict[str, Any]]) -> dict[str, dict[str, Any]]:
|
|
78
|
+
"""Filter out disabled components from a loaded modules dictionary.
|
|
79
|
+
|
|
80
|
+
This function is used to filter components that were loaded from index/cache,
|
|
81
|
+
since those bypass the dynamic loading filter.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
modules_dict: Dictionary mapping component types to their components
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Filtered dictionary with disabled components removed
|
|
88
|
+
"""
|
|
89
|
+
if not is_astra_cloud_environment():
|
|
90
|
+
return modules_dict
|
|
91
|
+
|
|
92
|
+
filtered_dict: dict[str, dict[str, Any]] = {}
|
|
93
|
+
for component_type, components in modules_dict.items():
|
|
94
|
+
disabled_set = ASTRA_CLOUD_DISABLED_COMPONENTS.get(component_type.lower(), set())
|
|
95
|
+
if disabled_set:
|
|
96
|
+
# Filter out disabled components
|
|
97
|
+
filtered_components = {name: comp for name, comp in components.items() if name not in disabled_set}
|
|
98
|
+
if filtered_components: # Only add if there are remaining components
|
|
99
|
+
filtered_dict[component_type] = filtered_components
|
|
100
|
+
else:
|
|
101
|
+
# No disabled components for this type, keep all
|
|
102
|
+
filtered_dict[component_type] = components
|
|
103
|
+
|
|
104
|
+
return filtered_dict
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lfx-nightly
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0.dev3
|
|
4
4
|
Summary: Langflow Executor - A lightweight CLI tool for executing and serving Langflow AI flows
|
|
5
5
|
Author-email: Gabriel Luiz Freitas Almeida <gabriel@langflow.org>
|
|
6
6
|
Requires-Python: <3.14,>=3.10
|
|
7
|
+
Requires-Dist: ag-ui-protocol>=0.1.10
|
|
7
8
|
Requires-Dist: aiofile<4.0.0,>=3.8.0
|
|
8
9
|
Requires-Dist: aiofiles<25.0.0,>=24.1.0
|
|
9
10
|
Requires-Dist: asyncer<1.0.0,>=0.0.8
|
|
10
11
|
Requires-Dist: cachetools>=6.0.0
|
|
11
12
|
Requires-Dist: chardet<6.0.0,>=5.2.0
|
|
13
|
+
Requires-Dist: cryptography>=43.0.0
|
|
12
14
|
Requires-Dist: defusedxml<1.0.0,>=0.7.1
|
|
13
15
|
Requires-Dist: docstring-parser<1.0.0,>=0.16
|
|
14
16
|
Requires-Dist: emoji<3.0.0,>=2.14.1
|
|
@@ -257,7 +259,10 @@ async def get_graph() -> Graph:
|
|
|
257
259
|
uv pip install lfx
|
|
258
260
|
|
|
259
261
|
# Install additional dependencies required for the agent
|
|
260
|
-
uv pip install langchain-
|
|
262
|
+
uv pip install 'langchain-core>=0.3.0,<1.0.0' \
|
|
263
|
+
'langchain-openai>=0.3.0,<1.0.0' \
|
|
264
|
+
'langchain-community>=0.3.0,<1.0.0' \
|
|
265
|
+
beautifulsoup4 lxml
|
|
261
266
|
```
|
|
262
267
|
|
|
263
268
|
**Step 3: Set up environment**
|