lfx-nightly 0.2.0.dev0__py3-none-any.whl → 0.2.0.dev41__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/_assets/component_index.json +1 -1
- lfx/base/agents/agent.py +21 -4
- lfx/base/agents/altk_base_agent.py +393 -0
- lfx/base/agents/altk_tool_wrappers.py +565 -0
- lfx/base/agents/events.py +2 -1
- lfx/base/composio/composio_base.py +159 -224
- lfx/base/data/base_file.py +97 -20
- lfx/base/data/docling_utils.py +61 -10
- lfx/base/data/storage_utils.py +301 -0
- lfx/base/data/utils.py +178 -14
- lfx/base/mcp/util.py +2 -2
- lfx/base/models/anthropic_constants.py +21 -12
- lfx/base/models/groq_constants.py +74 -58
- lfx/base/models/groq_model_discovery.py +265 -0
- lfx/base/models/model.py +1 -1
- lfx/base/models/model_utils.py +100 -0
- lfx/base/models/openai_constants.py +7 -0
- lfx/base/models/watsonx_constants.py +32 -8
- lfx/base/tools/run_flow.py +601 -129
- lfx/cli/commands.py +9 -4
- lfx/cli/common.py +2 -2
- lfx/cli/run.py +1 -1
- lfx/cli/script_loader.py +53 -11
- lfx/components/Notion/create_page.py +1 -1
- lfx/components/Notion/list_database_properties.py +1 -1
- lfx/components/Notion/list_pages.py +1 -1
- lfx/components/Notion/list_users.py +1 -1
- lfx/components/Notion/page_content_viewer.py +1 -1
- lfx/components/Notion/search.py +1 -1
- lfx/components/Notion/update_page_property.py +1 -1
- lfx/components/__init__.py +19 -5
- lfx/components/{agents → altk}/__init__.py +5 -9
- lfx/components/altk/altk_agent.py +193 -0
- lfx/components/apify/apify_actor.py +1 -1
- lfx/components/composio/__init__.py +70 -18
- lfx/components/composio/apollo_composio.py +11 -0
- lfx/components/composio/bitbucket_composio.py +11 -0
- lfx/components/composio/canva_composio.py +11 -0
- lfx/components/composio/coda_composio.py +11 -0
- lfx/components/composio/composio_api.py +10 -0
- lfx/components/composio/discord_composio.py +1 -1
- lfx/components/composio/elevenlabs_composio.py +11 -0
- lfx/components/composio/exa_composio.py +11 -0
- lfx/components/composio/firecrawl_composio.py +11 -0
- lfx/components/composio/fireflies_composio.py +11 -0
- lfx/components/composio/gmail_composio.py +1 -1
- lfx/components/composio/googlebigquery_composio.py +11 -0
- lfx/components/composio/googlecalendar_composio.py +1 -1
- lfx/components/composio/googledocs_composio.py +1 -1
- lfx/components/composio/googlemeet_composio.py +1 -1
- lfx/components/composio/googlesheets_composio.py +1 -1
- lfx/components/composio/googletasks_composio.py +1 -1
- lfx/components/composio/heygen_composio.py +11 -0
- lfx/components/composio/mem0_composio.py +11 -0
- lfx/components/composio/peopledatalabs_composio.py +11 -0
- lfx/components/composio/perplexityai_composio.py +11 -0
- lfx/components/composio/serpapi_composio.py +11 -0
- lfx/components/composio/slack_composio.py +3 -574
- lfx/components/composio/slackbot_composio.py +1 -1
- lfx/components/composio/snowflake_composio.py +11 -0
- lfx/components/composio/tavily_composio.py +11 -0
- lfx/components/composio/youtube_composio.py +2 -2
- lfx/components/cuga/__init__.py +34 -0
- lfx/components/cuga/cuga_agent.py +730 -0
- lfx/components/data/__init__.py +78 -28
- lfx/components/data_source/__init__.py +58 -0
- lfx/components/{data → data_source}/api_request.py +26 -3
- lfx/components/{data → data_source}/csv_to_data.py +15 -10
- lfx/components/{data → data_source}/json_to_data.py +15 -8
- lfx/components/{data → data_source}/news_search.py +1 -1
- lfx/components/{data → data_source}/rss.py +1 -1
- lfx/components/{data → data_source}/sql_executor.py +1 -1
- lfx/components/{data → data_source}/url.py +1 -1
- lfx/components/{data → data_source}/web_search.py +1 -1
- lfx/components/datastax/astradb_cql.py +1 -1
- lfx/components/datastax/astradb_graph.py +1 -1
- lfx/components/datastax/astradb_tool.py +1 -1
- lfx/components/datastax/astradb_vectorstore.py +1 -1
- lfx/components/datastax/hcd.py +1 -1
- lfx/components/deactivated/json_document_builder.py +1 -1
- lfx/components/docling/__init__.py +0 -3
- lfx/components/docling/chunk_docling_document.py +3 -1
- lfx/components/docling/export_docling_document.py +3 -1
- lfx/components/elastic/elasticsearch.py +1 -1
- lfx/components/files_and_knowledge/__init__.py +47 -0
- lfx/components/{data → files_and_knowledge}/directory.py +1 -1
- lfx/components/{data → files_and_knowledge}/file.py +304 -24
- lfx/components/{knowledge_bases → files_and_knowledge}/retrieval.py +2 -2
- lfx/components/{data → files_and_knowledge}/save_file.py +218 -31
- lfx/components/flow_controls/__init__.py +58 -0
- lfx/components/{logic → flow_controls}/conditional_router.py +1 -1
- lfx/components/{logic → flow_controls}/loop.py +43 -9
- lfx/components/flow_controls/run_flow.py +108 -0
- lfx/components/glean/glean_search_api.py +1 -1
- lfx/components/groq/groq.py +35 -28
- lfx/components/helpers/__init__.py +102 -0
- lfx/components/ibm/watsonx.py +7 -1
- lfx/components/input_output/__init__.py +3 -1
- lfx/components/input_output/chat.py +4 -3
- lfx/components/input_output/chat_output.py +10 -4
- lfx/components/input_output/text.py +1 -1
- lfx/components/input_output/text_output.py +1 -1
- lfx/components/{data → input_output}/webhook.py +1 -1
- lfx/components/knowledge_bases/__init__.py +59 -4
- lfx/components/langchain_utilities/character.py +1 -1
- lfx/components/langchain_utilities/csv_agent.py +84 -16
- lfx/components/langchain_utilities/json_agent.py +67 -12
- lfx/components/langchain_utilities/language_recursive.py +1 -1
- lfx/components/llm_operations/__init__.py +46 -0
- lfx/components/{processing → llm_operations}/batch_run.py +17 -8
- lfx/components/{processing → llm_operations}/lambda_filter.py +1 -1
- lfx/components/{logic → llm_operations}/llm_conditional_router.py +1 -1
- lfx/components/{processing/llm_router.py → llm_operations/llm_selector.py} +3 -3
- lfx/components/{processing → llm_operations}/structured_output.py +1 -1
- lfx/components/logic/__init__.py +126 -0
- lfx/components/mem0/mem0_chat_memory.py +11 -0
- lfx/components/models/__init__.py +64 -9
- lfx/components/models_and_agents/__init__.py +49 -0
- lfx/components/{agents → models_and_agents}/agent.py +6 -4
- lfx/components/models_and_agents/embedding_model.py +353 -0
- lfx/components/models_and_agents/language_model.py +398 -0
- lfx/components/{agents → models_and_agents}/mcp_component.py +53 -44
- lfx/components/{helpers → models_and_agents}/memory.py +1 -1
- lfx/components/nvidia/system_assist.py +1 -1
- lfx/components/olivya/olivya.py +1 -1
- lfx/components/ollama/ollama.py +24 -5
- lfx/components/processing/__init__.py +9 -60
- lfx/components/processing/converter.py +1 -1
- lfx/components/processing/dataframe_operations.py +1 -1
- lfx/components/processing/parse_json_data.py +2 -2
- lfx/components/processing/parser.py +1 -1
- lfx/components/processing/split_text.py +1 -1
- lfx/components/qdrant/qdrant.py +1 -1
- lfx/components/redis/redis.py +1 -1
- lfx/components/twelvelabs/split_video.py +10 -0
- lfx/components/twelvelabs/video_file.py +12 -0
- lfx/components/utilities/__init__.py +43 -0
- lfx/components/{helpers → utilities}/calculator_core.py +1 -1
- lfx/components/{helpers → utilities}/current_date.py +1 -1
- lfx/components/{processing → utilities}/python_repl_core.py +1 -1
- lfx/components/vectorstores/local_db.py +9 -0
- lfx/components/youtube/youtube_transcripts.py +118 -30
- lfx/custom/custom_component/component.py +57 -1
- lfx/custom/custom_component/custom_component.py +68 -6
- lfx/custom/directory_reader/directory_reader.py +5 -2
- lfx/graph/edge/base.py +43 -20
- lfx/graph/state/model.py +15 -2
- lfx/graph/utils.py +6 -0
- lfx/graph/vertex/param_handler.py +10 -7
- lfx/helpers/__init__.py +12 -0
- lfx/helpers/flow.py +117 -0
- lfx/inputs/input_mixin.py +24 -1
- lfx/inputs/inputs.py +13 -1
- lfx/interface/components.py +161 -83
- lfx/log/logger.py +5 -3
- lfx/schema/image.py +2 -12
- lfx/services/database/__init__.py +5 -0
- lfx/services/database/service.py +25 -0
- lfx/services/deps.py +87 -22
- lfx/services/interfaces.py +5 -0
- lfx/services/manager.py +24 -10
- lfx/services/mcp_composer/service.py +1029 -162
- lfx/services/session.py +5 -0
- lfx/services/settings/auth.py +18 -11
- lfx/services/settings/base.py +56 -30
- lfx/services/settings/constants.py +8 -0
- lfx/services/storage/local.py +108 -46
- lfx/services/storage/service.py +171 -29
- lfx/template/field/base.py +3 -0
- lfx/utils/image.py +29 -11
- lfx/utils/ssrf_protection.py +384 -0
- lfx/utils/validate_cloud.py +26 -0
- {lfx_nightly-0.2.0.dev0.dist-info → lfx_nightly-0.2.0.dev41.dist-info}/METADATA +38 -22
- {lfx_nightly-0.2.0.dev0.dist-info → lfx_nightly-0.2.0.dev41.dist-info}/RECORD +189 -160
- {lfx_nightly-0.2.0.dev0.dist-info → lfx_nightly-0.2.0.dev41.dist-info}/WHEEL +1 -1
- lfx/components/agents/altk_agent.py +0 -366
- lfx/components/agents/cuga_agent.py +0 -1013
- lfx/components/docling/docling_remote_vlm.py +0 -284
- lfx/components/logic/run_flow.py +0 -71
- lfx/components/models/embedding_model.py +0 -195
- lfx/components/models/language_model.py +0 -144
- lfx/components/processing/dataframe_to_toolset.py +0 -259
- /lfx/components/{data → data_source}/mock_data.py +0 -0
- /lfx/components/{knowledge_bases → files_and_knowledge}/ingestion.py +0 -0
- /lfx/components/{logic → flow_controls}/data_conditional_router.py +0 -0
- /lfx/components/{logic → flow_controls}/flow_tool.py +0 -0
- /lfx/components/{logic → flow_controls}/listen.py +0 -0
- /lfx/components/{logic → flow_controls}/notify.py +0 -0
- /lfx/components/{logic → flow_controls}/pass_message.py +0 -0
- /lfx/components/{logic → flow_controls}/sub_flow.py +0 -0
- /lfx/components/{processing → models_and_agents}/prompt.py +0 -0
- /lfx/components/{helpers → processing}/create_list.py +0 -0
- /lfx/components/{helpers → processing}/output_parser.py +0 -0
- /lfx/components/{helpers → processing}/store_message.py +0 -0
- /lfx/components/{helpers → utilities}/id_generator.py +0 -0
- {lfx_nightly-0.2.0.dev0.dist-info → lfx_nightly-0.2.0.dev41.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from lfx.base.tools.run_flow import RunFlowBaseComponent
|
|
5
|
+
from lfx.log.logger import logger
|
|
6
|
+
from lfx.schema.data import Data
|
|
7
|
+
from lfx.schema.dotdict import dotdict
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RunFlowComponent(RunFlowBaseComponent):
|
|
11
|
+
display_name = "Run Flow"
|
|
12
|
+
description = (
|
|
13
|
+
"Executes another flow from within the same project. Can also be used as a tool for agents."
|
|
14
|
+
" \n **Select a Flow to use the tool mode**"
|
|
15
|
+
)
|
|
16
|
+
documentation: str = "https://docs.langflow.org/run-flow"
|
|
17
|
+
beta = True
|
|
18
|
+
name = "RunFlow"
|
|
19
|
+
icon = "Workflow"
|
|
20
|
+
|
|
21
|
+
inputs = RunFlowBaseComponent.get_base_inputs()
|
|
22
|
+
outputs = RunFlowBaseComponent.get_base_outputs()
|
|
23
|
+
|
|
24
|
+
async def update_build_config(
|
|
25
|
+
self,
|
|
26
|
+
build_config: dotdict,
|
|
27
|
+
field_value: Any,
|
|
28
|
+
field_name: str | None = None,
|
|
29
|
+
):
|
|
30
|
+
missing_keys = [key for key in self.default_keys if key not in build_config]
|
|
31
|
+
for key in missing_keys:
|
|
32
|
+
if key == "flow_name_selected":
|
|
33
|
+
build_config[key] = {"options": [], "options_metadata": [], "value": None}
|
|
34
|
+
elif key == "flow_id_selected":
|
|
35
|
+
build_config[key] = {"value": None}
|
|
36
|
+
elif key == "cache_flow":
|
|
37
|
+
build_config[key] = {"value": False}
|
|
38
|
+
else:
|
|
39
|
+
build_config[key] = {}
|
|
40
|
+
if field_name == "flow_name_selected" and (build_config.get("is_refresh", False) or field_value is None):
|
|
41
|
+
# refresh button was clicked or componented was initialized, so list the flows
|
|
42
|
+
options: list[str] = await self.alist_flows_by_flow_folder()
|
|
43
|
+
build_config["flow_name_selected"]["options"] = [flow.data["name"] for flow in options]
|
|
44
|
+
build_config["flow_name_selected"]["options_metadata"] = []
|
|
45
|
+
for flow in options:
|
|
46
|
+
# populate options_metadata
|
|
47
|
+
build_config["flow_name_selected"]["options_metadata"].append(
|
|
48
|
+
{"id": flow.data["id"], "updated_at": flow.data["updated_at"]}
|
|
49
|
+
)
|
|
50
|
+
# update selected flow if it is stale
|
|
51
|
+
if str(flow.data["id"]) == self.flow_id_selected:
|
|
52
|
+
await self.check_and_update_stale_flow(flow, build_config)
|
|
53
|
+
elif field_name in {"flow_name_selected", "flow_id_selected"} and field_value is not None:
|
|
54
|
+
# flow was selected by name or id, so get the flow and update the bcfg
|
|
55
|
+
try:
|
|
56
|
+
# derive flow id if the field_name is flow_name_selected
|
|
57
|
+
build_config["flow_id_selected"]["value"] = (
|
|
58
|
+
self.get_selected_flow_meta(build_config, "id") or build_config["flow_id_selected"]["value"]
|
|
59
|
+
)
|
|
60
|
+
updated_at = self.get_selected_flow_meta(build_config, "updated_at")
|
|
61
|
+
await self.load_graph_and_update_cfg(
|
|
62
|
+
build_config, build_config["flow_id_selected"]["value"], updated_at
|
|
63
|
+
)
|
|
64
|
+
except Exception as e:
|
|
65
|
+
msg = f"Error building graph for flow {field_value}"
|
|
66
|
+
await logger.aexception(msg)
|
|
67
|
+
raise RuntimeError(msg) from e
|
|
68
|
+
|
|
69
|
+
return build_config
|
|
70
|
+
|
|
71
|
+
def get_selected_flow_meta(self, build_config: dotdict, field: str) -> dict:
|
|
72
|
+
"""Get the selected flow's metadata from the build config."""
|
|
73
|
+
return build_config.get("flow_name_selected", {}).get("selected_metadata", {}).get(field)
|
|
74
|
+
|
|
75
|
+
async def load_graph_and_update_cfg(
|
|
76
|
+
self,
|
|
77
|
+
build_config: dotdict,
|
|
78
|
+
flow_id: str,
|
|
79
|
+
updated_at: str | datetime,
|
|
80
|
+
) -> None:
|
|
81
|
+
"""Load a flow's graph and update the build config."""
|
|
82
|
+
graph = await self.get_graph(
|
|
83
|
+
flow_id_selected=flow_id,
|
|
84
|
+
updated_at=self.get_str_isots(updated_at),
|
|
85
|
+
)
|
|
86
|
+
self.update_build_config_from_graph(build_config, graph)
|
|
87
|
+
|
|
88
|
+
def should_update_stale_flow(self, flow: Data, build_config: dotdict) -> bool:
|
|
89
|
+
"""Check if the flow should be updated."""
|
|
90
|
+
return (
|
|
91
|
+
(updated_at := self.get_str_isots(flow.data["updated_at"])) # true updated_at date just fetched from db
|
|
92
|
+
and (stale_at := self.get_selected_flow_meta(build_config, "updated_at")) # outdated date in bcfg
|
|
93
|
+
and self._parse_timestamp(updated_at) > self._parse_timestamp(stale_at) # stale flow condition
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
async def check_and_update_stale_flow(self, flow: Data, build_config: dotdict) -> None:
|
|
97
|
+
"""Check if the flow should be updated and update it if necessary."""
|
|
98
|
+
# TODO: improve contract/return value
|
|
99
|
+
if self.should_update_stale_flow(flow, build_config):
|
|
100
|
+
await self.load_graph_and_update_cfg(
|
|
101
|
+
build_config,
|
|
102
|
+
flow.data["id"],
|
|
103
|
+
flow.data["updated_at"],
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
def get_str_isots(self, date: datetime | str) -> str:
|
|
107
|
+
"""Get a string timestamp from a datetime or string."""
|
|
108
|
+
return date.isoformat() if hasattr(date, "isoformat") else date
|
|
@@ -101,7 +101,7 @@ class GleanAPIWrapper(BaseModel):
|
|
|
101
101
|
class GleanSearchAPIComponent(LCToolComponent):
|
|
102
102
|
display_name: str = "Glean Search API"
|
|
103
103
|
description: str = "Search using Glean's API."
|
|
104
|
-
documentation: str = "https://docs.langflow.org/
|
|
104
|
+
documentation: str = "https://docs.langflow.org/bundles-glean"
|
|
105
105
|
icon: str = "Glean"
|
|
106
106
|
|
|
107
107
|
outputs = [
|
lfx/components/groq/groq.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import requests
|
|
2
1
|
from pydantic.v1 import SecretStr
|
|
3
2
|
|
|
4
|
-
from lfx.base.models.groq_constants import GROQ_MODELS
|
|
3
|
+
from lfx.base.models.groq_constants import GROQ_MODELS
|
|
4
|
+
from lfx.base.models.groq_model_discovery import get_groq_models
|
|
5
5
|
from lfx.base.models.model import LCModelComponent
|
|
6
6
|
from lfx.field_typing import LanguageModel
|
|
7
7
|
from lfx.field_typing.range_spec import RangeSpec
|
|
@@ -52,7 +52,7 @@ class GroqModel(LCModelComponent):
|
|
|
52
52
|
DropdownInput(
|
|
53
53
|
name="model_name",
|
|
54
54
|
display_name="Model",
|
|
55
|
-
info="The name of the model to use.",
|
|
55
|
+
info="The name of the model to use. Add your Groq API key to access additional available models.",
|
|
56
56
|
options=GROQ_MODELS,
|
|
57
57
|
value=GROQ_MODELS[0],
|
|
58
58
|
refresh_button=True,
|
|
@@ -71,35 +71,42 @@ class GroqModel(LCModelComponent):
|
|
|
71
71
|
]
|
|
72
72
|
|
|
73
73
|
def get_models(self, *, tool_model_enabled: bool | None = None) -> list[str]:
|
|
74
|
+
"""Get available Groq models using the dynamic discovery system.
|
|
75
|
+
|
|
76
|
+
This method uses the groq_model_discovery module which:
|
|
77
|
+
- Fetches models directly from Groq API
|
|
78
|
+
- Automatically tests tool calling support
|
|
79
|
+
- Caches results for 24 hours
|
|
80
|
+
- Falls back to hardcoded list if API fails
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
tool_model_enabled: If True, only return models that support tool calling
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
List of available model IDs
|
|
87
|
+
"""
|
|
74
88
|
try:
|
|
75
|
-
|
|
76
|
-
|
|
89
|
+
# Get models with metadata from dynamic discovery system
|
|
90
|
+
api_key = self.api_key if hasattr(self, "api_key") and self.api_key else None
|
|
91
|
+
models_metadata = get_groq_models(api_key=api_key)
|
|
77
92
|
|
|
78
|
-
|
|
79
|
-
response.raise_for_status()
|
|
80
|
-
model_list = response.json()
|
|
93
|
+
# Filter out non-LLM models (audio, TTS, guards)
|
|
81
94
|
model_ids = [
|
|
82
|
-
|
|
95
|
+
model_id for model_id, metadata in models_metadata.items() if not metadata.get("not_supported", False)
|
|
83
96
|
]
|
|
84
|
-
|
|
97
|
+
|
|
98
|
+
# Filter by tool calling support if requested
|
|
99
|
+
if tool_model_enabled:
|
|
100
|
+
model_ids = [model_id for model_id in model_ids if models_metadata[model_id].get("tool_calling", False)]
|
|
101
|
+
logger.info(f"Loaded {len(model_ids)} Groq models with tool calling support")
|
|
102
|
+
else:
|
|
103
|
+
logger.info(f"Loaded {len(model_ids)} Groq models")
|
|
104
|
+
except (ValueError, KeyError, TypeError, ImportError) as e:
|
|
85
105
|
logger.exception(f"Error getting model names: {e}")
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
from langchain_groq import ChatGroq
|
|
90
|
-
except ImportError as e:
|
|
91
|
-
msg = "langchain_groq is not installed. Please install it with `pip install langchain_groq`."
|
|
92
|
-
raise ImportError(msg) from e
|
|
93
|
-
for model in model_ids:
|
|
94
|
-
model_with_tool = ChatGroq(
|
|
95
|
-
model=model,
|
|
96
|
-
api_key=self.api_key,
|
|
97
|
-
base_url=self.base_url,
|
|
98
|
-
)
|
|
99
|
-
if not self.supports_tool_calling(model_with_tool) or model in TOOL_CALLING_UNSUPPORTED_GROQ_MODELS:
|
|
100
|
-
model_ids.remove(model)
|
|
106
|
+
# Fallback to hardcoded list from groq_constants.py
|
|
107
|
+
return GROQ_MODELS
|
|
108
|
+
else:
|
|
101
109
|
return model_ids
|
|
102
|
-
return model_ids
|
|
103
110
|
|
|
104
111
|
def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None):
|
|
105
112
|
if field_name in {"base_url", "model_name", "tool_model_enabled", "api_key"} and field_value:
|
|
@@ -107,13 +114,13 @@ class GroqModel(LCModelComponent):
|
|
|
107
114
|
if len(self.api_key) != 0:
|
|
108
115
|
try:
|
|
109
116
|
ids = self.get_models(tool_model_enabled=self.tool_model_enabled)
|
|
110
|
-
except (
|
|
117
|
+
except (ValueError, KeyError, TypeError, ImportError) as e:
|
|
111
118
|
logger.exception(f"Error getting model names: {e}")
|
|
112
119
|
ids = GROQ_MODELS
|
|
113
120
|
build_config.setdefault("model_name", {})
|
|
114
121
|
build_config["model_name"]["options"] = ids
|
|
115
122
|
build_config["model_name"].setdefault("value", ids[0])
|
|
116
|
-
except
|
|
123
|
+
except (ValueError, KeyError, TypeError, AttributeError) as e:
|
|
117
124
|
msg = f"Error getting model names: {e}"
|
|
118
125
|
raise ValueError(msg) from e
|
|
119
126
|
return build_config
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
"""Helpers module - backwards compatibility for moved components."""
|
|
2
|
+
|
|
1
3
|
from __future__ import annotations
|
|
2
4
|
|
|
5
|
+
import sys
|
|
3
6
|
from typing import TYPE_CHECKING, Any
|
|
4
7
|
|
|
5
8
|
from lfx.components._importing import import_mod
|
|
@@ -33,12 +36,111 @@ __all__ = [
|
|
|
33
36
|
"OutputParserComponent",
|
|
34
37
|
]
|
|
35
38
|
|
|
39
|
+
# Register redirected submodules in sys.modules for direct importlib.import_module() calls
|
|
40
|
+
# This allows imports like: import lfx.components.helpers.current_date
|
|
41
|
+
_redirected_submodules = {
|
|
42
|
+
"lfx.components.helpers.current_date": "lfx.components.utilities.current_date",
|
|
43
|
+
"lfx.components.helpers.calculator_core": "lfx.components.utilities.calculator_core",
|
|
44
|
+
"lfx.components.helpers.id_generator": "lfx.components.utilities.id_generator",
|
|
45
|
+
"lfx.components.helpers.memory": "lfx.components.models_and_agents.memory",
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
for old_path, new_path in _redirected_submodules.items():
|
|
49
|
+
if old_path not in sys.modules:
|
|
50
|
+
# Use a lazy loader that imports the actual module when accessed
|
|
51
|
+
class _RedirectedModule:
|
|
52
|
+
def __init__(self, target_path: str, original_path: str):
|
|
53
|
+
self._target_path = target_path
|
|
54
|
+
self._original_path = original_path
|
|
55
|
+
self._module = None
|
|
56
|
+
|
|
57
|
+
def __getattr__(self, name: str) -> Any:
|
|
58
|
+
if self._module is None:
|
|
59
|
+
from importlib import import_module
|
|
60
|
+
|
|
61
|
+
self._module = import_module(self._target_path)
|
|
62
|
+
# Also register under the original path for future imports
|
|
63
|
+
sys.modules[self._original_path] = self._module
|
|
64
|
+
return getattr(self._module, name)
|
|
65
|
+
|
|
66
|
+
def __repr__(self) -> str:
|
|
67
|
+
return f"<redirected module '{self._original_path}' -> '{self._target_path}'>"
|
|
68
|
+
|
|
69
|
+
sys.modules[old_path] = _RedirectedModule(new_path, old_path)
|
|
70
|
+
|
|
36
71
|
|
|
37
72
|
def __getattr__(attr_name: str) -> Any:
|
|
38
73
|
"""Lazily import helper components on attribute access."""
|
|
74
|
+
# Handle submodule access for backwards compatibility
|
|
75
|
+
# e.g., lfx.components.helpers.id_generator -> lfx.components.utilities.id_generator
|
|
76
|
+
if attr_name == "id_generator":
|
|
77
|
+
from importlib import import_module
|
|
78
|
+
|
|
79
|
+
result = import_module("lfx.components.utilities.id_generator")
|
|
80
|
+
globals()[attr_name] = result
|
|
81
|
+
return result
|
|
82
|
+
if attr_name == "calculator_core":
|
|
83
|
+
from importlib import import_module
|
|
84
|
+
|
|
85
|
+
result = import_module("lfx.components.utilities.calculator_core")
|
|
86
|
+
globals()[attr_name] = result
|
|
87
|
+
return result
|
|
88
|
+
if attr_name == "current_date":
|
|
89
|
+
from importlib import import_module
|
|
90
|
+
|
|
91
|
+
result = import_module("lfx.components.utilities.current_date")
|
|
92
|
+
globals()[attr_name] = result
|
|
93
|
+
return result
|
|
94
|
+
if attr_name == "memory":
|
|
95
|
+
from importlib import import_module
|
|
96
|
+
|
|
97
|
+
result = import_module("lfx.components.models_and_agents.memory")
|
|
98
|
+
globals()[attr_name] = result
|
|
99
|
+
return result
|
|
100
|
+
|
|
39
101
|
if attr_name not in _dynamic_imports:
|
|
40
102
|
msg = f"module '{__name__}' has no attribute '{attr_name}'"
|
|
41
103
|
raise AttributeError(msg)
|
|
104
|
+
|
|
105
|
+
# CurrentDateComponent, CalculatorComponent, and IDGeneratorComponent were moved to utilities
|
|
106
|
+
# Forward them to utilities for backwards compatibility
|
|
107
|
+
if attr_name in ("CurrentDateComponent", "CalculatorComponent", "IDGeneratorComponent"):
|
|
108
|
+
from lfx.components import utilities
|
|
109
|
+
|
|
110
|
+
result = getattr(utilities, attr_name)
|
|
111
|
+
globals()[attr_name] = result
|
|
112
|
+
return result
|
|
113
|
+
|
|
114
|
+
# MemoryComponent was moved to models_and_agents
|
|
115
|
+
# Forward it to models_and_agents for backwards compatibility
|
|
116
|
+
if attr_name == "MemoryComponent":
|
|
117
|
+
from lfx.components import models_and_agents
|
|
118
|
+
|
|
119
|
+
result = getattr(models_and_agents, attr_name)
|
|
120
|
+
globals()[attr_name] = result
|
|
121
|
+
return result
|
|
122
|
+
|
|
123
|
+
# CreateListComponent, MessageStoreComponent, and OutputParserComponent were moved to processing
|
|
124
|
+
# Forward them to processing for backwards compatibility
|
|
125
|
+
if attr_name == "CreateListComponent":
|
|
126
|
+
from lfx.components import processing
|
|
127
|
+
|
|
128
|
+
result = getattr(processing, attr_name)
|
|
129
|
+
globals()[attr_name] = result
|
|
130
|
+
return result
|
|
131
|
+
if attr_name == "MessageStoreComponent":
|
|
132
|
+
from lfx.components import processing
|
|
133
|
+
|
|
134
|
+
result = processing.MessageStoreComponent
|
|
135
|
+
globals()[attr_name] = result
|
|
136
|
+
return result
|
|
137
|
+
if attr_name == "OutputParserComponent":
|
|
138
|
+
from lfx.components import processing
|
|
139
|
+
|
|
140
|
+
result = getattr(processing, attr_name)
|
|
141
|
+
globals()[attr_name] = result
|
|
142
|
+
return result
|
|
143
|
+
|
|
42
144
|
try:
|
|
43
145
|
result = import_mod(attr_name, _dynamic_imports[attr_name], __spec__.parent)
|
|
44
146
|
except (ModuleNotFoundError, ImportError, AttributeError) as e:
|
lfx/components/ibm/watsonx.py
CHANGED
|
@@ -197,8 +197,14 @@ class WatsonxAIComponent(LCModelComponent):
|
|
|
197
197
|
"logit_bias": logit_bias,
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
+
# Pass API key as plain string to avoid SecretStr serialization issues
|
|
201
|
+
# when model is configured with with_config() or used in batch operations
|
|
202
|
+
api_key_value = self.api_key
|
|
203
|
+
if isinstance(api_key_value, SecretStr):
|
|
204
|
+
api_key_value = api_key_value.get_secret_value()
|
|
205
|
+
|
|
200
206
|
return ChatWatsonx(
|
|
201
|
-
apikey=
|
|
207
|
+
apikey=api_key_value,
|
|
202
208
|
url=self.base_url,
|
|
203
209
|
project_id=self.project_id,
|
|
204
210
|
model_id=self.model_name,
|
|
@@ -9,15 +9,17 @@ if TYPE_CHECKING:
|
|
|
9
9
|
from lfx.components.input_output.chat_output import ChatOutput
|
|
10
10
|
from lfx.components.input_output.text import TextInputComponent
|
|
11
11
|
from lfx.components.input_output.text_output import TextOutputComponent
|
|
12
|
+
from lfx.components.input_output.webhook import WebhookComponent
|
|
12
13
|
|
|
13
14
|
_dynamic_imports = {
|
|
14
15
|
"ChatInput": "chat",
|
|
15
16
|
"ChatOutput": "chat_output",
|
|
16
17
|
"TextInputComponent": "text",
|
|
17
18
|
"TextOutputComponent": "text_output",
|
|
19
|
+
"WebhookComponent": "webhook",
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
__all__ = ["ChatInput", "ChatOutput", "TextInputComponent", "TextOutputComponent"]
|
|
22
|
+
__all__ = ["ChatInput", "ChatOutput", "TextInputComponent", "TextOutputComponent", "WebhookComponent"]
|
|
21
23
|
|
|
22
24
|
|
|
23
25
|
def __getattr__(attr_name: str) -> Any:
|
|
@@ -19,7 +19,7 @@ from lfx.utils.constants import (
|
|
|
19
19
|
class ChatInput(ChatComponent):
|
|
20
20
|
display_name = "Chat Input"
|
|
21
21
|
description = "Get chat inputs from the Playground."
|
|
22
|
-
documentation: str = "https://docs.langflow.org/
|
|
22
|
+
documentation: str = "https://docs.langflow.org/chat-input-and-output"
|
|
23
23
|
icon = "MessagesSquare"
|
|
24
24
|
name = "ChatInput"
|
|
25
25
|
minimized = True
|
|
@@ -89,15 +89,16 @@ class ChatInput(ChatComponent):
|
|
|
89
89
|
# Filter out None/empty values
|
|
90
90
|
files = [f for f in files if f is not None and f != ""]
|
|
91
91
|
|
|
92
|
+
session_id = self.session_id or self.graph.session_id or ""
|
|
92
93
|
message = await Message.create(
|
|
93
94
|
text=self.input_value,
|
|
94
95
|
sender=self.sender,
|
|
95
96
|
sender_name=self.sender_name,
|
|
96
|
-
session_id=
|
|
97
|
+
session_id=session_id,
|
|
97
98
|
context_id=self.context_id,
|
|
98
99
|
files=files,
|
|
99
100
|
)
|
|
100
|
-
if
|
|
101
|
+
if session_id and isinstance(message, Message) and self.should_store_message:
|
|
101
102
|
stored_message = await self.send_message(
|
|
102
103
|
message,
|
|
103
104
|
)
|
|
@@ -22,7 +22,7 @@ from lfx.utils.constants import (
|
|
|
22
22
|
class ChatOutput(ChatComponent):
|
|
23
23
|
display_name = "Chat Output"
|
|
24
24
|
description = "Display a chat message in the Playground."
|
|
25
|
-
documentation: str = "https://docs.langflow.org/
|
|
25
|
+
documentation: str = "https://docs.langflow.org/chat-input-and-output"
|
|
26
26
|
icon = "MessagesSquare"
|
|
27
27
|
name = "ChatOutput"
|
|
28
28
|
minimized = True
|
|
@@ -117,23 +117,29 @@ class ChatOutput(ChatComponent):
|
|
|
117
117
|
source, _, display_name, source_id = self.get_properties_from_source_component()
|
|
118
118
|
|
|
119
119
|
# Create or use existing Message object
|
|
120
|
-
if isinstance(self.input_value, Message):
|
|
120
|
+
if isinstance(self.input_value, Message) and not self.is_connected_to_chat_input():
|
|
121
121
|
message = self.input_value
|
|
122
122
|
# Update message properties
|
|
123
123
|
message.text = text
|
|
124
|
+
# Preserve existing session_id from the incoming message if it exists
|
|
125
|
+
existing_session_id = message.session_id
|
|
124
126
|
else:
|
|
125
127
|
message = Message(text=text)
|
|
128
|
+
existing_session_id = None
|
|
126
129
|
|
|
127
130
|
# Set message properties
|
|
128
131
|
message.sender = self.sender
|
|
129
132
|
message.sender_name = self.sender_name
|
|
130
|
-
|
|
133
|
+
# Preserve session_id from incoming message, or use component/graph session_id
|
|
134
|
+
message.session_id = (
|
|
135
|
+
self.session_id or existing_session_id or (self.graph.session_id if hasattr(self, "graph") else None) or ""
|
|
136
|
+
)
|
|
131
137
|
message.context_id = self.context_id
|
|
132
138
|
message.flow_id = self.graph.flow_id if hasattr(self, "graph") else None
|
|
133
139
|
message.properties.source = self._build_source(source_id, display_name, source)
|
|
134
140
|
|
|
135
141
|
# Store message if needed
|
|
136
|
-
if
|
|
142
|
+
if message.session_id and self.should_store_message:
|
|
137
143
|
stored_message = await self.send_message(message)
|
|
138
144
|
self.message.value = stored_message
|
|
139
145
|
message = stored_message
|
|
@@ -6,7 +6,7 @@ from lfx.schema.message import Message
|
|
|
6
6
|
class TextInputComponent(TextComponent):
|
|
7
7
|
display_name = "Text Input"
|
|
8
8
|
description = "Get user text inputs."
|
|
9
|
-
documentation: str = "https://docs.langflow.org/
|
|
9
|
+
documentation: str = "https://docs.langflow.org/text-input-and-output"
|
|
10
10
|
icon = "type"
|
|
11
11
|
name = "TextInput"
|
|
12
12
|
|
|
@@ -6,7 +6,7 @@ from lfx.schema.message import Message
|
|
|
6
6
|
class TextOutputComponent(TextComponent):
|
|
7
7
|
display_name = "Text Output"
|
|
8
8
|
description = "Sends text output via API."
|
|
9
|
-
documentation: str = "https://docs.langflow.org/
|
|
9
|
+
documentation: str = "https://docs.langflow.org/text-input-and-output"
|
|
10
10
|
icon = "type"
|
|
11
11
|
name = "TextOutput"
|
|
12
12
|
|
|
@@ -7,7 +7,7 @@ from lfx.schema.data import Data
|
|
|
7
7
|
|
|
8
8
|
class WebhookComponent(Component):
|
|
9
9
|
display_name = "Webhook"
|
|
10
|
-
documentation: str = "https://docs.langflow.org/
|
|
10
|
+
documentation: str = "https://docs.langflow.org/component-webhook"
|
|
11
11
|
name = "Webhook"
|
|
12
12
|
icon = "webhook"
|
|
13
13
|
|
|
@@ -1,12 +1,19 @@
|
|
|
1
|
+
"""Knowledge bases module - backwards compatibility alias for files_and_knowledge.
|
|
2
|
+
|
|
3
|
+
This module provides backwards compatibility by forwarding all imports
|
|
4
|
+
to files_and_knowledge where the actual knowledge base components are located.
|
|
5
|
+
"""
|
|
6
|
+
|
|
1
7
|
from __future__ import annotations
|
|
2
8
|
|
|
9
|
+
import sys
|
|
3
10
|
from typing import TYPE_CHECKING, Any
|
|
4
11
|
|
|
5
12
|
from lfx.components._importing import import_mod
|
|
6
13
|
|
|
7
14
|
if TYPE_CHECKING:
|
|
8
|
-
from lfx.components.
|
|
9
|
-
from lfx.components.
|
|
15
|
+
from lfx.components.files_and_knowledge.ingestion import KnowledgeIngestionComponent
|
|
16
|
+
from lfx.components.files_and_knowledge.retrieval import KnowledgeRetrievalComponent
|
|
10
17
|
|
|
11
18
|
_dynamic_imports = {
|
|
12
19
|
"KnowledgeIngestionComponent": "ingestion",
|
|
@@ -15,14 +22,61 @@ _dynamic_imports = {
|
|
|
15
22
|
|
|
16
23
|
__all__ = ["KnowledgeIngestionComponent", "KnowledgeRetrievalComponent"]
|
|
17
24
|
|
|
25
|
+
# Register redirected submodules in sys.modules for direct importlib.import_module() calls
|
|
26
|
+
# This allows imports like: import lfx.components.knowledge_bases.ingestion
|
|
27
|
+
_redirected_submodules = {
|
|
28
|
+
"lfx.components.knowledge_bases.ingestion": "lfx.components.files_and_knowledge.ingestion",
|
|
29
|
+
"lfx.components.knowledge_bases.retrieval": "lfx.components.files_and_knowledge.retrieval",
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
for old_path, new_path in _redirected_submodules.items():
|
|
33
|
+
if old_path not in sys.modules:
|
|
34
|
+
# Use a lazy loader that imports the actual module when accessed
|
|
35
|
+
class _RedirectedModule:
|
|
36
|
+
def __init__(self, target_path: str, original_path: str):
|
|
37
|
+
self._target_path = target_path
|
|
38
|
+
self._original_path = original_path
|
|
39
|
+
self._module = None
|
|
40
|
+
|
|
41
|
+
def __getattr__(self, name: str) -> Any:
|
|
42
|
+
if self._module is None:
|
|
43
|
+
from importlib import import_module
|
|
44
|
+
|
|
45
|
+
self._module = import_module(self._target_path)
|
|
46
|
+
# Also register under the original path for future imports
|
|
47
|
+
sys.modules[self._original_path] = self._module
|
|
48
|
+
return getattr(self._module, name)
|
|
49
|
+
|
|
50
|
+
def __repr__(self) -> str:
|
|
51
|
+
return f"<redirected module '{self._original_path}' -> '{self._target_path}'>"
|
|
52
|
+
|
|
53
|
+
sys.modules[old_path] = _RedirectedModule(new_path, old_path)
|
|
54
|
+
|
|
18
55
|
|
|
19
56
|
def __getattr__(attr_name: str) -> Any:
|
|
20
|
-
"""
|
|
57
|
+
"""Forward attribute access to files_and_knowledge components."""
|
|
58
|
+
# Handle submodule access for backwards compatibility
|
|
59
|
+
if attr_name == "ingestion":
|
|
60
|
+
from importlib import import_module
|
|
61
|
+
|
|
62
|
+
result = import_module("lfx.components.files_and_knowledge.ingestion")
|
|
63
|
+
globals()[attr_name] = result
|
|
64
|
+
return result
|
|
65
|
+
if attr_name == "retrieval":
|
|
66
|
+
from importlib import import_module
|
|
67
|
+
|
|
68
|
+
result = import_module("lfx.components.files_and_knowledge.retrieval")
|
|
69
|
+
globals()[attr_name] = result
|
|
70
|
+
return result
|
|
71
|
+
|
|
21
72
|
if attr_name not in _dynamic_imports:
|
|
22
73
|
msg = f"module '{__name__}' has no attribute '{attr_name}'"
|
|
23
74
|
raise AttributeError(msg)
|
|
75
|
+
|
|
76
|
+
# Import from files_and_knowledge using the correct package path
|
|
77
|
+
package = "lfx.components.files_and_knowledge"
|
|
24
78
|
try:
|
|
25
|
-
result = import_mod(attr_name, _dynamic_imports[attr_name],
|
|
79
|
+
result = import_mod(attr_name, _dynamic_imports[attr_name], package)
|
|
26
80
|
except (ModuleNotFoundError, ImportError, AttributeError) as e:
|
|
27
81
|
msg = f"Could not import '{attr_name}' from '{__name__}': {e}"
|
|
28
82
|
raise AttributeError(msg) from e
|
|
@@ -31,4 +85,5 @@ def __getattr__(attr_name: str) -> Any:
|
|
|
31
85
|
|
|
32
86
|
|
|
33
87
|
def __dir__() -> list[str]:
|
|
88
|
+
"""Return directory of available components."""
|
|
34
89
|
return list(__all__)
|
|
@@ -10,7 +10,7 @@ from lfx.utils.util import unescape_string
|
|
|
10
10
|
class CharacterTextSplitterComponent(LCTextSplitterComponent):
|
|
11
11
|
display_name = "Character Text Splitter"
|
|
12
12
|
description = "Split text by number of characters."
|
|
13
|
-
documentation = "https://docs.langflow.org/
|
|
13
|
+
documentation = "https://docs.langflow.org/bundles-langchain"
|
|
14
14
|
name = "CharacterTextSplitter"
|
|
15
15
|
icon = "LangChain"
|
|
16
16
|
|