lfx-nightly 0.2.0.dev25__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.
Potentially problematic release.
This version of lfx-nightly might be problematic. Click here for more details.
- lfx/__init__.py +0 -0
- lfx/__main__.py +25 -0
- lfx/_assets/component_index.json +1 -0
- lfx/base/__init__.py +0 -0
- lfx/base/agents/__init__.py +0 -0
- lfx/base/agents/agent.py +375 -0
- lfx/base/agents/altk_base_agent.py +380 -0
- lfx/base/agents/altk_tool_wrappers.py +565 -0
- lfx/base/agents/callback.py +130 -0
- lfx/base/agents/context.py +109 -0
- lfx/base/agents/crewai/__init__.py +0 -0
- lfx/base/agents/crewai/crew.py +231 -0
- lfx/base/agents/crewai/tasks.py +12 -0
- lfx/base/agents/default_prompts.py +23 -0
- lfx/base/agents/errors.py +15 -0
- lfx/base/agents/events.py +430 -0
- lfx/base/agents/utils.py +237 -0
- lfx/base/astra_assistants/__init__.py +0 -0
- lfx/base/astra_assistants/util.py +171 -0
- lfx/base/chains/__init__.py +0 -0
- lfx/base/chains/model.py +19 -0
- lfx/base/composio/__init__.py +0 -0
- lfx/base/composio/composio_base.py +2584 -0
- lfx/base/compressors/__init__.py +0 -0
- lfx/base/compressors/model.py +60 -0
- lfx/base/constants.py +46 -0
- lfx/base/curl/__init__.py +0 -0
- lfx/base/curl/parse.py +188 -0
- lfx/base/data/__init__.py +5 -0
- lfx/base/data/base_file.py +810 -0
- lfx/base/data/docling_utils.py +338 -0
- lfx/base/data/storage_utils.py +192 -0
- lfx/base/data/utils.py +362 -0
- lfx/base/datastax/__init__.py +5 -0
- lfx/base/datastax/astradb_base.py +896 -0
- lfx/base/document_transformers/__init__.py +0 -0
- lfx/base/document_transformers/model.py +43 -0
- lfx/base/embeddings/__init__.py +0 -0
- lfx/base/embeddings/aiml_embeddings.py +62 -0
- lfx/base/embeddings/embeddings_class.py +113 -0
- lfx/base/embeddings/model.py +26 -0
- lfx/base/flow_processing/__init__.py +0 -0
- lfx/base/flow_processing/utils.py +86 -0
- lfx/base/huggingface/__init__.py +0 -0
- lfx/base/huggingface/model_bridge.py +133 -0
- lfx/base/io/__init__.py +0 -0
- lfx/base/io/chat.py +21 -0
- lfx/base/io/text.py +22 -0
- lfx/base/knowledge_bases/__init__.py +3 -0
- lfx/base/knowledge_bases/knowledge_base_utils.py +137 -0
- lfx/base/langchain_utilities/__init__.py +0 -0
- lfx/base/langchain_utilities/model.py +35 -0
- lfx/base/langchain_utilities/spider_constants.py +1 -0
- lfx/base/langwatch/__init__.py +0 -0
- lfx/base/langwatch/utils.py +18 -0
- lfx/base/mcp/__init__.py +0 -0
- lfx/base/mcp/constants.py +2 -0
- lfx/base/mcp/util.py +1659 -0
- lfx/base/memory/__init__.py +0 -0
- lfx/base/memory/memory.py +49 -0
- lfx/base/memory/model.py +38 -0
- lfx/base/models/__init__.py +3 -0
- lfx/base/models/aiml_constants.py +51 -0
- lfx/base/models/anthropic_constants.py +51 -0
- lfx/base/models/aws_constants.py +151 -0
- lfx/base/models/chat_result.py +76 -0
- lfx/base/models/cometapi_constants.py +54 -0
- lfx/base/models/google_generative_ai_constants.py +70 -0
- lfx/base/models/google_generative_ai_model.py +38 -0
- lfx/base/models/groq_constants.py +150 -0
- lfx/base/models/groq_model_discovery.py +265 -0
- lfx/base/models/model.py +375 -0
- lfx/base/models/model_input_constants.py +378 -0
- lfx/base/models/model_metadata.py +41 -0
- lfx/base/models/model_utils.py +108 -0
- lfx/base/models/novita_constants.py +35 -0
- lfx/base/models/ollama_constants.py +52 -0
- lfx/base/models/openai_constants.py +129 -0
- lfx/base/models/sambanova_constants.py +18 -0
- lfx/base/models/watsonx_constants.py +36 -0
- lfx/base/processing/__init__.py +0 -0
- lfx/base/prompts/__init__.py +0 -0
- lfx/base/prompts/api_utils.py +224 -0
- lfx/base/prompts/utils.py +61 -0
- lfx/base/textsplitters/__init__.py +0 -0
- lfx/base/textsplitters/model.py +28 -0
- lfx/base/tools/__init__.py +0 -0
- lfx/base/tools/base.py +26 -0
- lfx/base/tools/component_tool.py +325 -0
- lfx/base/tools/constants.py +49 -0
- lfx/base/tools/flow_tool.py +132 -0
- lfx/base/tools/run_flow.py +698 -0
- lfx/base/vectorstores/__init__.py +0 -0
- lfx/base/vectorstores/model.py +193 -0
- lfx/base/vectorstores/utils.py +22 -0
- lfx/base/vectorstores/vector_store_connection_decorator.py +52 -0
- lfx/cli/__init__.py +5 -0
- lfx/cli/commands.py +327 -0
- lfx/cli/common.py +650 -0
- lfx/cli/run.py +506 -0
- lfx/cli/script_loader.py +289 -0
- lfx/cli/serve_app.py +546 -0
- lfx/cli/validation.py +69 -0
- lfx/components/FAISS/__init__.py +34 -0
- lfx/components/FAISS/faiss.py +111 -0
- lfx/components/Notion/__init__.py +19 -0
- lfx/components/Notion/add_content_to_page.py +269 -0
- lfx/components/Notion/create_page.py +94 -0
- lfx/components/Notion/list_database_properties.py +68 -0
- lfx/components/Notion/list_pages.py +122 -0
- lfx/components/Notion/list_users.py +77 -0
- lfx/components/Notion/page_content_viewer.py +93 -0
- lfx/components/Notion/search.py +111 -0
- lfx/components/Notion/update_page_property.py +114 -0
- lfx/components/__init__.py +428 -0
- lfx/components/_importing.py +42 -0
- lfx/components/agentql/__init__.py +3 -0
- lfx/components/agentql/agentql_api.py +151 -0
- lfx/components/aiml/__init__.py +37 -0
- lfx/components/aiml/aiml.py +115 -0
- lfx/components/aiml/aiml_embeddings.py +37 -0
- lfx/components/altk/__init__.py +34 -0
- lfx/components/altk/altk_agent.py +193 -0
- lfx/components/amazon/__init__.py +36 -0
- lfx/components/amazon/amazon_bedrock_converse.py +195 -0
- lfx/components/amazon/amazon_bedrock_embedding.py +109 -0
- lfx/components/amazon/amazon_bedrock_model.py +130 -0
- lfx/components/amazon/s3_bucket_uploader.py +211 -0
- lfx/components/anthropic/__init__.py +34 -0
- lfx/components/anthropic/anthropic.py +187 -0
- lfx/components/apify/__init__.py +5 -0
- lfx/components/apify/apify_actor.py +325 -0
- lfx/components/arxiv/__init__.py +3 -0
- lfx/components/arxiv/arxiv.py +169 -0
- lfx/components/assemblyai/__init__.py +46 -0
- lfx/components/assemblyai/assemblyai_get_subtitles.py +83 -0
- lfx/components/assemblyai/assemblyai_lemur.py +183 -0
- lfx/components/assemblyai/assemblyai_list_transcripts.py +95 -0
- lfx/components/assemblyai/assemblyai_poll_transcript.py +72 -0
- lfx/components/assemblyai/assemblyai_start_transcript.py +188 -0
- lfx/components/azure/__init__.py +37 -0
- lfx/components/azure/azure_openai.py +95 -0
- lfx/components/azure/azure_openai_embeddings.py +83 -0
- lfx/components/baidu/__init__.py +32 -0
- lfx/components/baidu/baidu_qianfan_chat.py +113 -0
- lfx/components/bing/__init__.py +3 -0
- lfx/components/bing/bing_search_api.py +61 -0
- lfx/components/cassandra/__init__.py +40 -0
- lfx/components/cassandra/cassandra.py +264 -0
- lfx/components/cassandra/cassandra_chat.py +92 -0
- lfx/components/cassandra/cassandra_graph.py +238 -0
- lfx/components/chains/__init__.py +3 -0
- lfx/components/chroma/__init__.py +34 -0
- lfx/components/chroma/chroma.py +169 -0
- lfx/components/cleanlab/__init__.py +40 -0
- lfx/components/cleanlab/cleanlab_evaluator.py +155 -0
- lfx/components/cleanlab/cleanlab_rag_evaluator.py +254 -0
- lfx/components/cleanlab/cleanlab_remediator.py +131 -0
- lfx/components/clickhouse/__init__.py +34 -0
- lfx/components/clickhouse/clickhouse.py +135 -0
- lfx/components/cloudflare/__init__.py +32 -0
- lfx/components/cloudflare/cloudflare.py +81 -0
- lfx/components/cohere/__init__.py +40 -0
- lfx/components/cohere/cohere_embeddings.py +81 -0
- lfx/components/cohere/cohere_models.py +46 -0
- lfx/components/cohere/cohere_rerank.py +51 -0
- lfx/components/cometapi/__init__.py +32 -0
- lfx/components/cometapi/cometapi.py +166 -0
- lfx/components/composio/__init__.py +222 -0
- lfx/components/composio/agentql_composio.py +11 -0
- lfx/components/composio/agiled_composio.py +11 -0
- lfx/components/composio/airtable_composio.py +11 -0
- lfx/components/composio/apollo_composio.py +11 -0
- lfx/components/composio/asana_composio.py +11 -0
- lfx/components/composio/attio_composio.py +11 -0
- lfx/components/composio/bitbucket_composio.py +11 -0
- lfx/components/composio/bolna_composio.py +11 -0
- lfx/components/composio/brightdata_composio.py +11 -0
- lfx/components/composio/calendly_composio.py +11 -0
- lfx/components/composio/canva_composio.py +11 -0
- lfx/components/composio/canvas_composio.py +11 -0
- lfx/components/composio/coda_composio.py +11 -0
- lfx/components/composio/composio_api.py +278 -0
- lfx/components/composio/contentful_composio.py +11 -0
- lfx/components/composio/digicert_composio.py +11 -0
- lfx/components/composio/discord_composio.py +11 -0
- lfx/components/composio/dropbox_compnent.py +11 -0
- lfx/components/composio/elevenlabs_composio.py +11 -0
- lfx/components/composio/exa_composio.py +11 -0
- lfx/components/composio/figma_composio.py +11 -0
- lfx/components/composio/finage_composio.py +11 -0
- lfx/components/composio/firecrawl_composio.py +11 -0
- lfx/components/composio/fireflies_composio.py +11 -0
- lfx/components/composio/fixer_composio.py +11 -0
- lfx/components/composio/flexisign_composio.py +11 -0
- lfx/components/composio/freshdesk_composio.py +11 -0
- lfx/components/composio/github_composio.py +11 -0
- lfx/components/composio/gmail_composio.py +38 -0
- lfx/components/composio/googlebigquery_composio.py +11 -0
- lfx/components/composio/googlecalendar_composio.py +11 -0
- lfx/components/composio/googleclassroom_composio.py +11 -0
- lfx/components/composio/googledocs_composio.py +11 -0
- lfx/components/composio/googlemeet_composio.py +11 -0
- lfx/components/composio/googlesheets_composio.py +11 -0
- lfx/components/composio/googletasks_composio.py +8 -0
- lfx/components/composio/heygen_composio.py +11 -0
- lfx/components/composio/instagram_composio.py +11 -0
- lfx/components/composio/jira_composio.py +11 -0
- lfx/components/composio/jotform_composio.py +11 -0
- lfx/components/composio/klaviyo_composio.py +11 -0
- lfx/components/composio/linear_composio.py +11 -0
- lfx/components/composio/listennotes_composio.py +11 -0
- lfx/components/composio/mem0_composio.py +11 -0
- lfx/components/composio/miro_composio.py +11 -0
- lfx/components/composio/missive_composio.py +11 -0
- lfx/components/composio/notion_composio.py +11 -0
- lfx/components/composio/onedrive_composio.py +11 -0
- lfx/components/composio/outlook_composio.py +11 -0
- lfx/components/composio/pandadoc_composio.py +11 -0
- lfx/components/composio/peopledatalabs_composio.py +11 -0
- lfx/components/composio/perplexityai_composio.py +11 -0
- lfx/components/composio/reddit_composio.py +11 -0
- lfx/components/composio/serpapi_composio.py +11 -0
- lfx/components/composio/slack_composio.py +11 -0
- lfx/components/composio/slackbot_composio.py +11 -0
- lfx/components/composio/snowflake_composio.py +11 -0
- lfx/components/composio/supabase_composio.py +11 -0
- lfx/components/composio/tavily_composio.py +11 -0
- lfx/components/composio/timelinesai_composio.py +11 -0
- lfx/components/composio/todoist_composio.py +11 -0
- lfx/components/composio/wrike_composio.py +11 -0
- lfx/components/composio/youtube_composio.py +11 -0
- lfx/components/confluence/__init__.py +3 -0
- lfx/components/confluence/confluence.py +84 -0
- lfx/components/couchbase/__init__.py +34 -0
- lfx/components/couchbase/couchbase.py +102 -0
- lfx/components/crewai/__init__.py +49 -0
- lfx/components/crewai/crewai.py +108 -0
- lfx/components/crewai/hierarchical_crew.py +47 -0
- lfx/components/crewai/hierarchical_task.py +45 -0
- lfx/components/crewai/sequential_crew.py +53 -0
- lfx/components/crewai/sequential_task.py +74 -0
- lfx/components/crewai/sequential_task_agent.py +144 -0
- lfx/components/cuga/__init__.py +34 -0
- lfx/components/cuga/cuga_agent.py +730 -0
- lfx/components/custom_component/__init__.py +34 -0
- lfx/components/custom_component/custom_component.py +31 -0
- lfx/components/data/__init__.py +114 -0
- lfx/components/data_source/__init__.py +58 -0
- lfx/components/data_source/api_request.py +577 -0
- lfx/components/data_source/csv_to_data.py +101 -0
- lfx/components/data_source/json_to_data.py +106 -0
- lfx/components/data_source/mock_data.py +398 -0
- lfx/components/data_source/news_search.py +166 -0
- lfx/components/data_source/rss.py +71 -0
- lfx/components/data_source/sql_executor.py +101 -0
- lfx/components/data_source/url.py +311 -0
- lfx/components/data_source/web_search.py +326 -0
- lfx/components/datastax/__init__.py +76 -0
- lfx/components/datastax/astradb_assistant_manager.py +307 -0
- lfx/components/datastax/astradb_chatmemory.py +40 -0
- lfx/components/datastax/astradb_cql.py +288 -0
- lfx/components/datastax/astradb_graph.py +217 -0
- lfx/components/datastax/astradb_tool.py +378 -0
- lfx/components/datastax/astradb_vectorize.py +122 -0
- lfx/components/datastax/astradb_vectorstore.py +449 -0
- lfx/components/datastax/create_assistant.py +59 -0
- lfx/components/datastax/create_thread.py +33 -0
- lfx/components/datastax/dotenv.py +36 -0
- lfx/components/datastax/get_assistant.py +38 -0
- lfx/components/datastax/getenvvar.py +31 -0
- lfx/components/datastax/graph_rag.py +141 -0
- lfx/components/datastax/hcd.py +315 -0
- lfx/components/datastax/list_assistants.py +26 -0
- lfx/components/datastax/run.py +90 -0
- lfx/components/deactivated/__init__.py +15 -0
- lfx/components/deactivated/amazon_kendra.py +66 -0
- lfx/components/deactivated/chat_litellm_model.py +158 -0
- lfx/components/deactivated/code_block_extractor.py +26 -0
- lfx/components/deactivated/documents_to_data.py +22 -0
- lfx/components/deactivated/embed.py +16 -0
- lfx/components/deactivated/extract_key_from_data.py +46 -0
- lfx/components/deactivated/json_document_builder.py +57 -0
- lfx/components/deactivated/list_flows.py +20 -0
- lfx/components/deactivated/mcp_sse.py +61 -0
- lfx/components/deactivated/mcp_stdio.py +62 -0
- lfx/components/deactivated/merge_data.py +93 -0
- lfx/components/deactivated/message.py +37 -0
- lfx/components/deactivated/metal.py +54 -0
- lfx/components/deactivated/multi_query.py +59 -0
- lfx/components/deactivated/retriever.py +43 -0
- lfx/components/deactivated/selective_passthrough.py +77 -0
- lfx/components/deactivated/should_run_next.py +40 -0
- lfx/components/deactivated/split_text.py +63 -0
- lfx/components/deactivated/store_message.py +24 -0
- lfx/components/deactivated/sub_flow.py +124 -0
- lfx/components/deactivated/vectara_self_query.py +76 -0
- lfx/components/deactivated/vector_store.py +24 -0
- lfx/components/deepseek/__init__.py +34 -0
- lfx/components/deepseek/deepseek.py +136 -0
- lfx/components/docling/__init__.py +43 -0
- lfx/components/docling/chunk_docling_document.py +186 -0
- lfx/components/docling/docling_inline.py +238 -0
- lfx/components/docling/docling_remote.py +195 -0
- lfx/components/docling/export_docling_document.py +117 -0
- lfx/components/documentloaders/__init__.py +3 -0
- lfx/components/duckduckgo/__init__.py +3 -0
- lfx/components/duckduckgo/duck_duck_go_search_run.py +92 -0
- lfx/components/elastic/__init__.py +37 -0
- lfx/components/elastic/elasticsearch.py +267 -0
- lfx/components/elastic/opensearch.py +789 -0
- lfx/components/elastic/opensearch_multimodal.py +1575 -0
- lfx/components/embeddings/__init__.py +37 -0
- lfx/components/embeddings/similarity.py +77 -0
- lfx/components/embeddings/text_embedder.py +65 -0
- lfx/components/exa/__init__.py +3 -0
- lfx/components/exa/exa_search.py +68 -0
- lfx/components/files_and_knowledge/__init__.py +47 -0
- lfx/components/files_and_knowledge/directory.py +113 -0
- lfx/components/files_and_knowledge/file.py +841 -0
- lfx/components/files_and_knowledge/ingestion.py +694 -0
- lfx/components/files_and_knowledge/retrieval.py +264 -0
- lfx/components/files_and_knowledge/save_file.py +746 -0
- lfx/components/firecrawl/__init__.py +43 -0
- lfx/components/firecrawl/firecrawl_crawl_api.py +88 -0
- lfx/components/firecrawl/firecrawl_extract_api.py +136 -0
- lfx/components/firecrawl/firecrawl_map_api.py +89 -0
- lfx/components/firecrawl/firecrawl_scrape_api.py +73 -0
- lfx/components/flow_controls/__init__.py +58 -0
- lfx/components/flow_controls/conditional_router.py +208 -0
- lfx/components/flow_controls/data_conditional_router.py +126 -0
- lfx/components/flow_controls/flow_tool.py +111 -0
- lfx/components/flow_controls/listen.py +29 -0
- lfx/components/flow_controls/loop.py +163 -0
- lfx/components/flow_controls/notify.py +88 -0
- lfx/components/flow_controls/pass_message.py +36 -0
- lfx/components/flow_controls/run_flow.py +108 -0
- lfx/components/flow_controls/sub_flow.py +115 -0
- lfx/components/git/__init__.py +4 -0
- lfx/components/git/git.py +262 -0
- lfx/components/git/gitextractor.py +196 -0
- lfx/components/glean/__init__.py +3 -0
- lfx/components/glean/glean_search_api.py +173 -0
- lfx/components/google/__init__.py +17 -0
- lfx/components/google/gmail.py +193 -0
- lfx/components/google/google_bq_sql_executor.py +157 -0
- lfx/components/google/google_drive.py +92 -0
- lfx/components/google/google_drive_search.py +152 -0
- lfx/components/google/google_generative_ai.py +144 -0
- lfx/components/google/google_generative_ai_embeddings.py +141 -0
- lfx/components/google/google_oauth_token.py +89 -0
- lfx/components/google/google_search_api_core.py +68 -0
- lfx/components/google/google_serper_api_core.py +74 -0
- lfx/components/groq/__init__.py +34 -0
- lfx/components/groq/groq.py +143 -0
- lfx/components/helpers/__init__.py +154 -0
- lfx/components/homeassistant/__init__.py +7 -0
- lfx/components/homeassistant/home_assistant_control.py +152 -0
- lfx/components/homeassistant/list_home_assistant_states.py +137 -0
- lfx/components/huggingface/__init__.py +37 -0
- lfx/components/huggingface/huggingface.py +199 -0
- lfx/components/huggingface/huggingface_inference_api.py +106 -0
- lfx/components/ibm/__init__.py +34 -0
- lfx/components/ibm/watsonx.py +207 -0
- lfx/components/ibm/watsonx_embeddings.py +135 -0
- lfx/components/icosacomputing/__init__.py +5 -0
- lfx/components/icosacomputing/combinatorial_reasoner.py +84 -0
- lfx/components/input_output/__init__.py +40 -0
- lfx/components/input_output/chat.py +109 -0
- lfx/components/input_output/chat_output.py +184 -0
- lfx/components/input_output/text.py +27 -0
- lfx/components/input_output/text_output.py +29 -0
- lfx/components/input_output/webhook.py +56 -0
- lfx/components/jigsawstack/__init__.py +23 -0
- lfx/components/jigsawstack/ai_scrape.py +126 -0
- lfx/components/jigsawstack/ai_web_search.py +136 -0
- lfx/components/jigsawstack/file_read.py +115 -0
- lfx/components/jigsawstack/file_upload.py +94 -0
- lfx/components/jigsawstack/image_generation.py +205 -0
- lfx/components/jigsawstack/nsfw.py +60 -0
- lfx/components/jigsawstack/object_detection.py +124 -0
- lfx/components/jigsawstack/sentiment.py +112 -0
- lfx/components/jigsawstack/text_to_sql.py +90 -0
- lfx/components/jigsawstack/text_translate.py +77 -0
- lfx/components/jigsawstack/vocr.py +107 -0
- lfx/components/knowledge_bases/__init__.py +89 -0
- lfx/components/langchain_utilities/__init__.py +109 -0
- lfx/components/langchain_utilities/character.py +53 -0
- lfx/components/langchain_utilities/conversation.py +59 -0
- lfx/components/langchain_utilities/csv_agent.py +175 -0
- lfx/components/langchain_utilities/fake_embeddings.py +26 -0
- lfx/components/langchain_utilities/html_link_extractor.py +35 -0
- lfx/components/langchain_utilities/json_agent.py +100 -0
- lfx/components/langchain_utilities/langchain_hub.py +126 -0
- lfx/components/langchain_utilities/language_recursive.py +49 -0
- lfx/components/langchain_utilities/language_semantic.py +138 -0
- lfx/components/langchain_utilities/llm_checker.py +39 -0
- lfx/components/langchain_utilities/llm_math.py +42 -0
- lfx/components/langchain_utilities/natural_language.py +61 -0
- lfx/components/langchain_utilities/openai_tools.py +53 -0
- lfx/components/langchain_utilities/openapi.py +48 -0
- lfx/components/langchain_utilities/recursive_character.py +60 -0
- lfx/components/langchain_utilities/retrieval_qa.py +83 -0
- lfx/components/langchain_utilities/runnable_executor.py +137 -0
- lfx/components/langchain_utilities/self_query.py +80 -0
- lfx/components/langchain_utilities/spider.py +142 -0
- lfx/components/langchain_utilities/sql.py +40 -0
- lfx/components/langchain_utilities/sql_database.py +35 -0
- lfx/components/langchain_utilities/sql_generator.py +78 -0
- lfx/components/langchain_utilities/tool_calling.py +59 -0
- lfx/components/langchain_utilities/vector_store_info.py +49 -0
- lfx/components/langchain_utilities/vector_store_router.py +33 -0
- lfx/components/langchain_utilities/xml_agent.py +71 -0
- lfx/components/langwatch/__init__.py +3 -0
- lfx/components/langwatch/langwatch.py +278 -0
- lfx/components/link_extractors/__init__.py +3 -0
- lfx/components/llm_operations/__init__.py +46 -0
- lfx/components/llm_operations/batch_run.py +205 -0
- lfx/components/llm_operations/lambda_filter.py +218 -0
- lfx/components/llm_operations/llm_conditional_router.py +421 -0
- lfx/components/llm_operations/llm_selector.py +499 -0
- lfx/components/llm_operations/structured_output.py +244 -0
- lfx/components/lmstudio/__init__.py +34 -0
- lfx/components/lmstudio/lmstudioembeddings.py +89 -0
- lfx/components/lmstudio/lmstudiomodel.py +133 -0
- lfx/components/logic/__init__.py +181 -0
- lfx/components/maritalk/__init__.py +32 -0
- lfx/components/maritalk/maritalk.py +52 -0
- lfx/components/mem0/__init__.py +3 -0
- lfx/components/mem0/mem0_chat_memory.py +147 -0
- lfx/components/milvus/__init__.py +34 -0
- lfx/components/milvus/milvus.py +115 -0
- lfx/components/mistral/__init__.py +37 -0
- lfx/components/mistral/mistral.py +114 -0
- lfx/components/mistral/mistral_embeddings.py +58 -0
- lfx/components/models/__init__.py +89 -0
- lfx/components/models_and_agents/__init__.py +49 -0
- lfx/components/models_and_agents/agent.py +644 -0
- lfx/components/models_and_agents/embedding_model.py +423 -0
- lfx/components/models_and_agents/language_model.py +398 -0
- lfx/components/models_and_agents/mcp_component.py +594 -0
- lfx/components/models_and_agents/memory.py +268 -0
- lfx/components/models_and_agents/prompt.py +67 -0
- lfx/components/mongodb/__init__.py +34 -0
- lfx/components/mongodb/mongodb_atlas.py +213 -0
- lfx/components/needle/__init__.py +3 -0
- lfx/components/needle/needle.py +104 -0
- lfx/components/notdiamond/__init__.py +34 -0
- lfx/components/notdiamond/notdiamond.py +228 -0
- lfx/components/novita/__init__.py +32 -0
- lfx/components/novita/novita.py +130 -0
- lfx/components/nvidia/__init__.py +57 -0
- lfx/components/nvidia/nvidia.py +151 -0
- lfx/components/nvidia/nvidia_embedding.py +77 -0
- lfx/components/nvidia/nvidia_ingest.py +317 -0
- lfx/components/nvidia/nvidia_rerank.py +63 -0
- lfx/components/nvidia/system_assist.py +65 -0
- lfx/components/olivya/__init__.py +3 -0
- lfx/components/olivya/olivya.py +116 -0
- lfx/components/ollama/__init__.py +37 -0
- lfx/components/ollama/ollama.py +548 -0
- lfx/components/ollama/ollama_embeddings.py +103 -0
- lfx/components/openai/__init__.py +37 -0
- lfx/components/openai/openai.py +100 -0
- lfx/components/openai/openai_chat_model.py +176 -0
- lfx/components/openrouter/__init__.py +32 -0
- lfx/components/openrouter/openrouter.py +104 -0
- lfx/components/output_parsers/__init__.py +3 -0
- lfx/components/perplexity/__init__.py +34 -0
- lfx/components/perplexity/perplexity.py +75 -0
- lfx/components/pgvector/__init__.py +34 -0
- lfx/components/pgvector/pgvector.py +72 -0
- lfx/components/pinecone/__init__.py +34 -0
- lfx/components/pinecone/pinecone.py +134 -0
- lfx/components/processing/__init__.py +72 -0
- lfx/components/processing/alter_metadata.py +109 -0
- lfx/components/processing/combine_text.py +40 -0
- lfx/components/processing/converter.py +248 -0
- lfx/components/processing/create_data.py +111 -0
- lfx/components/processing/create_list.py +40 -0
- lfx/components/processing/data_operations.py +528 -0
- lfx/components/processing/data_to_dataframe.py +71 -0
- lfx/components/processing/dataframe_operations.py +313 -0
- lfx/components/processing/dataframe_to_toolset.py +259 -0
- lfx/components/processing/dynamic_create_data.py +357 -0
- lfx/components/processing/extract_key.py +54 -0
- lfx/components/processing/filter_data.py +43 -0
- lfx/components/processing/filter_data_values.py +89 -0
- lfx/components/processing/json_cleaner.py +104 -0
- lfx/components/processing/merge_data.py +91 -0
- lfx/components/processing/message_to_data.py +37 -0
- lfx/components/processing/output_parser.py +46 -0
- lfx/components/processing/parse_data.py +71 -0
- lfx/components/processing/parse_dataframe.py +69 -0
- lfx/components/processing/parse_json_data.py +91 -0
- lfx/components/processing/parser.py +148 -0
- lfx/components/processing/regex.py +83 -0
- lfx/components/processing/select_data.py +49 -0
- lfx/components/processing/split_text.py +141 -0
- lfx/components/processing/store_message.py +91 -0
- lfx/components/processing/update_data.py +161 -0
- lfx/components/prototypes/__init__.py +35 -0
- lfx/components/prototypes/python_function.py +73 -0
- lfx/components/qdrant/__init__.py +34 -0
- lfx/components/qdrant/qdrant.py +109 -0
- lfx/components/redis/__init__.py +37 -0
- lfx/components/redis/redis.py +89 -0
- lfx/components/redis/redis_chat.py +43 -0
- lfx/components/sambanova/__init__.py +32 -0
- lfx/components/sambanova/sambanova.py +84 -0
- lfx/components/scrapegraph/__init__.py +40 -0
- lfx/components/scrapegraph/scrapegraph_markdownify_api.py +64 -0
- lfx/components/scrapegraph/scrapegraph_search_api.py +64 -0
- lfx/components/scrapegraph/scrapegraph_smart_scraper_api.py +71 -0
- lfx/components/searchapi/__init__.py +34 -0
- lfx/components/searchapi/search.py +79 -0
- lfx/components/serpapi/__init__.py +3 -0
- lfx/components/serpapi/serp.py +115 -0
- lfx/components/supabase/__init__.py +34 -0
- lfx/components/supabase/supabase.py +76 -0
- lfx/components/tavily/__init__.py +4 -0
- lfx/components/tavily/tavily_extract.py +117 -0
- lfx/components/tavily/tavily_search.py +212 -0
- lfx/components/textsplitters/__init__.py +3 -0
- lfx/components/toolkits/__init__.py +3 -0
- lfx/components/tools/__init__.py +66 -0
- lfx/components/tools/calculator.py +109 -0
- lfx/components/tools/google_search_api.py +45 -0
- lfx/components/tools/google_serper_api.py +115 -0
- lfx/components/tools/python_code_structured_tool.py +328 -0
- lfx/components/tools/python_repl.py +98 -0
- lfx/components/tools/search_api.py +88 -0
- lfx/components/tools/searxng.py +145 -0
- lfx/components/tools/serp_api.py +120 -0
- lfx/components/tools/tavily_search_tool.py +345 -0
- lfx/components/tools/wikidata_api.py +103 -0
- lfx/components/tools/wikipedia_api.py +50 -0
- lfx/components/tools/yahoo_finance.py +130 -0
- lfx/components/twelvelabs/__init__.py +52 -0
- lfx/components/twelvelabs/convert_astra_results.py +84 -0
- lfx/components/twelvelabs/pegasus_index.py +311 -0
- lfx/components/twelvelabs/split_video.py +301 -0
- lfx/components/twelvelabs/text_embeddings.py +57 -0
- lfx/components/twelvelabs/twelvelabs_pegasus.py +408 -0
- lfx/components/twelvelabs/video_embeddings.py +100 -0
- lfx/components/twelvelabs/video_file.py +191 -0
- lfx/components/unstructured/__init__.py +3 -0
- lfx/components/unstructured/unstructured.py +121 -0
- lfx/components/upstash/__init__.py +34 -0
- lfx/components/upstash/upstash.py +124 -0
- lfx/components/utilities/__init__.py +43 -0
- lfx/components/utilities/calculator_core.py +89 -0
- lfx/components/utilities/current_date.py +42 -0
- lfx/components/utilities/id_generator.py +42 -0
- lfx/components/utilities/python_repl_core.py +98 -0
- lfx/components/vectara/__init__.py +37 -0
- lfx/components/vectara/vectara.py +97 -0
- lfx/components/vectara/vectara_rag.py +164 -0
- lfx/components/vectorstores/__init__.py +34 -0
- lfx/components/vectorstores/local_db.py +270 -0
- lfx/components/vertexai/__init__.py +37 -0
- lfx/components/vertexai/vertexai.py +71 -0
- lfx/components/vertexai/vertexai_embeddings.py +67 -0
- lfx/components/vlmrun/__init__.py +34 -0
- lfx/components/vlmrun/vlmrun_transcription.py +224 -0
- lfx/components/weaviate/__init__.py +34 -0
- lfx/components/weaviate/weaviate.py +89 -0
- lfx/components/wikipedia/__init__.py +4 -0
- lfx/components/wikipedia/wikidata.py +86 -0
- lfx/components/wikipedia/wikipedia.py +53 -0
- lfx/components/wolframalpha/__init__.py +3 -0
- lfx/components/wolframalpha/wolfram_alpha_api.py +54 -0
- lfx/components/xai/__init__.py +32 -0
- lfx/components/xai/xai.py +167 -0
- lfx/components/yahoosearch/__init__.py +3 -0
- lfx/components/yahoosearch/yahoo.py +137 -0
- lfx/components/youtube/__init__.py +52 -0
- lfx/components/youtube/channel.py +227 -0
- lfx/components/youtube/comments.py +231 -0
- lfx/components/youtube/playlist.py +33 -0
- lfx/components/youtube/search.py +120 -0
- lfx/components/youtube/trending.py +285 -0
- lfx/components/youtube/video_details.py +263 -0
- lfx/components/youtube/youtube_transcripts.py +206 -0
- lfx/components/zep/__init__.py +3 -0
- lfx/components/zep/zep.py +45 -0
- lfx/constants.py +6 -0
- lfx/custom/__init__.py +7 -0
- lfx/custom/attributes.py +87 -0
- lfx/custom/code_parser/__init__.py +3 -0
- lfx/custom/code_parser/code_parser.py +361 -0
- lfx/custom/custom_component/__init__.py +0 -0
- lfx/custom/custom_component/base_component.py +128 -0
- lfx/custom/custom_component/component.py +1890 -0
- lfx/custom/custom_component/component_with_cache.py +8 -0
- lfx/custom/custom_component/custom_component.py +650 -0
- lfx/custom/dependency_analyzer.py +165 -0
- lfx/custom/directory_reader/__init__.py +3 -0
- lfx/custom/directory_reader/directory_reader.py +359 -0
- lfx/custom/directory_reader/utils.py +171 -0
- lfx/custom/eval.py +12 -0
- lfx/custom/schema.py +32 -0
- lfx/custom/tree_visitor.py +21 -0
- lfx/custom/utils.py +877 -0
- lfx/custom/validate.py +523 -0
- lfx/events/__init__.py +1 -0
- lfx/events/event_manager.py +110 -0
- lfx/exceptions/__init__.py +0 -0
- lfx/exceptions/component.py +15 -0
- lfx/field_typing/__init__.py +91 -0
- lfx/field_typing/constants.py +216 -0
- lfx/field_typing/range_spec.py +35 -0
- lfx/graph/__init__.py +6 -0
- lfx/graph/edge/__init__.py +0 -0
- lfx/graph/edge/base.py +300 -0
- lfx/graph/edge/schema.py +119 -0
- lfx/graph/edge/utils.py +0 -0
- lfx/graph/graph/__init__.py +0 -0
- lfx/graph/graph/ascii.py +202 -0
- lfx/graph/graph/base.py +2298 -0
- lfx/graph/graph/constants.py +63 -0
- lfx/graph/graph/runnable_vertices_manager.py +133 -0
- lfx/graph/graph/schema.py +53 -0
- lfx/graph/graph/state_model.py +66 -0
- lfx/graph/graph/utils.py +1024 -0
- lfx/graph/schema.py +75 -0
- lfx/graph/state/__init__.py +0 -0
- lfx/graph/state/model.py +250 -0
- lfx/graph/utils.py +206 -0
- lfx/graph/vertex/__init__.py +0 -0
- lfx/graph/vertex/base.py +826 -0
- lfx/graph/vertex/constants.py +0 -0
- lfx/graph/vertex/exceptions.py +4 -0
- lfx/graph/vertex/param_handler.py +316 -0
- lfx/graph/vertex/schema.py +26 -0
- lfx/graph/vertex/utils.py +19 -0
- lfx/graph/vertex/vertex_types.py +489 -0
- lfx/helpers/__init__.py +141 -0
- lfx/helpers/base_model.py +71 -0
- lfx/helpers/custom.py +13 -0
- lfx/helpers/data.py +167 -0
- lfx/helpers/flow.py +308 -0
- lfx/inputs/__init__.py +68 -0
- lfx/inputs/constants.py +2 -0
- lfx/inputs/input_mixin.py +352 -0
- lfx/inputs/inputs.py +718 -0
- lfx/inputs/validators.py +19 -0
- lfx/interface/__init__.py +6 -0
- lfx/interface/components.py +897 -0
- lfx/interface/importing/__init__.py +5 -0
- lfx/interface/importing/utils.py +39 -0
- lfx/interface/initialize/__init__.py +3 -0
- lfx/interface/initialize/loading.py +317 -0
- lfx/interface/listing.py +26 -0
- lfx/interface/run.py +16 -0
- lfx/interface/utils.py +111 -0
- lfx/io/__init__.py +63 -0
- lfx/io/schema.py +295 -0
- lfx/load/__init__.py +8 -0
- lfx/load/load.py +256 -0
- lfx/load/utils.py +99 -0
- lfx/log/__init__.py +5 -0
- lfx/log/logger.py +411 -0
- lfx/logging/__init__.py +11 -0
- lfx/logging/logger.py +24 -0
- lfx/memory/__init__.py +70 -0
- lfx/memory/stubs.py +302 -0
- lfx/processing/__init__.py +1 -0
- lfx/processing/process.py +238 -0
- lfx/processing/utils.py +25 -0
- lfx/py.typed +0 -0
- lfx/schema/__init__.py +66 -0
- lfx/schema/artifact.py +83 -0
- lfx/schema/content_block.py +62 -0
- lfx/schema/content_types.py +91 -0
- lfx/schema/cross_module.py +80 -0
- lfx/schema/data.py +309 -0
- lfx/schema/dataframe.py +210 -0
- lfx/schema/dotdict.py +74 -0
- lfx/schema/encoders.py +13 -0
- lfx/schema/graph.py +47 -0
- lfx/schema/image.py +184 -0
- lfx/schema/json_schema.py +186 -0
- lfx/schema/log.py +62 -0
- lfx/schema/message.py +493 -0
- lfx/schema/openai_responses_schemas.py +74 -0
- lfx/schema/properties.py +41 -0
- lfx/schema/schema.py +180 -0
- lfx/schema/serialize.py +13 -0
- lfx/schema/table.py +142 -0
- lfx/schema/validators.py +114 -0
- lfx/serialization/__init__.py +5 -0
- lfx/serialization/constants.py +2 -0
- lfx/serialization/serialization.py +314 -0
- lfx/services/__init__.py +26 -0
- lfx/services/base.py +28 -0
- lfx/services/cache/__init__.py +6 -0
- lfx/services/cache/base.py +183 -0
- lfx/services/cache/service.py +166 -0
- lfx/services/cache/utils.py +169 -0
- lfx/services/chat/__init__.py +1 -0
- lfx/services/chat/config.py +2 -0
- lfx/services/chat/schema.py +10 -0
- lfx/services/database/__init__.py +5 -0
- lfx/services/database/service.py +25 -0
- lfx/services/deps.py +194 -0
- lfx/services/factory.py +19 -0
- lfx/services/initialize.py +19 -0
- lfx/services/interfaces.py +103 -0
- lfx/services/manager.py +185 -0
- lfx/services/mcp_composer/__init__.py +6 -0
- lfx/services/mcp_composer/factory.py +16 -0
- lfx/services/mcp_composer/service.py +1441 -0
- lfx/services/schema.py +21 -0
- lfx/services/session.py +87 -0
- lfx/services/settings/__init__.py +3 -0
- lfx/services/settings/auth.py +133 -0
- lfx/services/settings/base.py +668 -0
- lfx/services/settings/constants.py +43 -0
- lfx/services/settings/factory.py +23 -0
- lfx/services/settings/feature_flags.py +11 -0
- lfx/services/settings/service.py +35 -0
- lfx/services/settings/utils.py +40 -0
- lfx/services/shared_component_cache/__init__.py +1 -0
- lfx/services/shared_component_cache/factory.py +30 -0
- lfx/services/shared_component_cache/service.py +9 -0
- lfx/services/storage/__init__.py +5 -0
- lfx/services/storage/local.py +185 -0
- lfx/services/storage/service.py +177 -0
- lfx/services/tracing/__init__.py +1 -0
- lfx/services/tracing/service.py +21 -0
- lfx/settings.py +6 -0
- lfx/template/__init__.py +6 -0
- lfx/template/field/__init__.py +0 -0
- lfx/template/field/base.py +260 -0
- lfx/template/field/prompt.py +15 -0
- lfx/template/frontend_node/__init__.py +6 -0
- lfx/template/frontend_node/base.py +214 -0
- lfx/template/frontend_node/constants.py +65 -0
- lfx/template/frontend_node/custom_components.py +79 -0
- lfx/template/template/__init__.py +0 -0
- lfx/template/template/base.py +100 -0
- lfx/template/utils.py +217 -0
- lfx/type_extraction/__init__.py +19 -0
- lfx/type_extraction/type_extraction.py +75 -0
- lfx/type_extraction.py +80 -0
- lfx/utils/__init__.py +1 -0
- lfx/utils/async_helpers.py +42 -0
- lfx/utils/component_utils.py +154 -0
- lfx/utils/concurrency.py +60 -0
- lfx/utils/connection_string_parser.py +11 -0
- lfx/utils/constants.py +233 -0
- lfx/utils/data_structure.py +212 -0
- lfx/utils/exceptions.py +22 -0
- lfx/utils/helpers.py +34 -0
- lfx/utils/image.py +79 -0
- lfx/utils/langflow_utils.py +52 -0
- lfx/utils/lazy_load.py +15 -0
- lfx/utils/request_utils.py +18 -0
- lfx/utils/schemas.py +139 -0
- lfx/utils/ssrf_protection.py +384 -0
- lfx/utils/util.py +626 -0
- lfx/utils/util_strings.py +56 -0
- lfx/utils/validate_cloud.py +26 -0
- lfx/utils/version.py +24 -0
- lfx_nightly-0.2.0.dev25.dist-info/METADATA +312 -0
- lfx_nightly-0.2.0.dev25.dist-info/RECORD +769 -0
- lfx_nightly-0.2.0.dev25.dist-info/WHEEL +4 -0
- lfx_nightly-0.2.0.dev25.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,897 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import hashlib
|
|
3
|
+
import importlib
|
|
4
|
+
import inspect
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import pkgutil
|
|
8
|
+
import time
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
11
|
+
|
|
12
|
+
import orjson
|
|
13
|
+
|
|
14
|
+
from lfx.constants import BASE_COMPONENTS_PATH
|
|
15
|
+
from lfx.custom.utils import abuild_custom_components, create_component_template
|
|
16
|
+
from lfx.log.logger import logger
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from lfx.services.settings.service import SettingsService
|
|
20
|
+
|
|
21
|
+
MIN_MODULE_PARTS = 2
|
|
22
|
+
EXPECTED_RESULT_LENGTH = 2 # Expected length of the tuple returned by _process_single_module
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Create a class to manage component cache instead of using globals
|
|
26
|
+
class ComponentCache:
|
|
27
|
+
def __init__(self):
|
|
28
|
+
"""Initializes the component cache.
|
|
29
|
+
|
|
30
|
+
Creates empty storage for all component types and tracking of fully loaded components.
|
|
31
|
+
"""
|
|
32
|
+
self.all_types_dict: dict[str, Any] | None = None
|
|
33
|
+
self.fully_loaded_components: dict[str, bool] = {}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# Singleton instance
|
|
37
|
+
component_cache = ComponentCache()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _parse_dev_mode() -> tuple[bool, list[str] | None]:
|
|
41
|
+
"""Parse LFX_DEV to determine dev mode and which modules to load.
|
|
42
|
+
|
|
43
|
+
Development mode must be explicitly enabled via the LFX_DEV environment variable.
|
|
44
|
+
When enabled, components are always rebuilt dynamically to reflect code changes.
|
|
45
|
+
When disabled or not set, the prebuilt index is used for fast startup.
|
|
46
|
+
|
|
47
|
+
Supports two modes:
|
|
48
|
+
- Boolean mode: LFX_DEV=1/true/yes loads all modules dynamically
|
|
49
|
+
- List mode: LFX_DEV=mistral,openai,anthropic loads only specified modules
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Tuple of (dev_mode_enabled, module_list)
|
|
53
|
+
- If module_list is None, load all modules
|
|
54
|
+
- If module_list is a list, only load those specific modules
|
|
55
|
+
"""
|
|
56
|
+
lfx_dev = os.getenv("LFX_DEV", "").strip()
|
|
57
|
+
if not lfx_dev:
|
|
58
|
+
return (False, None)
|
|
59
|
+
|
|
60
|
+
# Boolean mode: "1", "true", "yes" enables dev mode
|
|
61
|
+
if lfx_dev.lower() in {"1", "true", "yes"}:
|
|
62
|
+
return (True, None) # Load all modules
|
|
63
|
+
|
|
64
|
+
# Boolean mode: "0", "false", "no" explicitly disables dev mode
|
|
65
|
+
if lfx_dev.lower() in {"0", "false", "no"}:
|
|
66
|
+
return (False, None)
|
|
67
|
+
|
|
68
|
+
# List mode: comma-separated values
|
|
69
|
+
modules = [m.strip().lower() for m in lfx_dev.split(",") if m.strip()]
|
|
70
|
+
if modules:
|
|
71
|
+
return (True, modules)
|
|
72
|
+
|
|
73
|
+
return (False, None)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _read_component_index(custom_path: str | None = None) -> dict | None:
|
|
77
|
+
"""Read and validate the prebuilt component index.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
custom_path: Optional custom path or URL to index file. If None, uses built-in index.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
The index dictionary if valid, None otherwise
|
|
84
|
+
"""
|
|
85
|
+
try:
|
|
86
|
+
import lfx
|
|
87
|
+
|
|
88
|
+
# Determine index location
|
|
89
|
+
if custom_path:
|
|
90
|
+
# Check if it's a URL
|
|
91
|
+
if custom_path.startswith(("http://", "https://")):
|
|
92
|
+
# Fetch from URL
|
|
93
|
+
import httpx
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
response = httpx.get(custom_path, timeout=10.0)
|
|
97
|
+
response.raise_for_status()
|
|
98
|
+
blob = orjson.loads(response.content)
|
|
99
|
+
except httpx.HTTPError as e:
|
|
100
|
+
logger.warning(f"Failed to fetch component index from {custom_path}: {e}")
|
|
101
|
+
return None
|
|
102
|
+
except orjson.JSONDecodeError as e:
|
|
103
|
+
logger.warning(f"Component index from {custom_path} is corrupted or invalid JSON: {e}")
|
|
104
|
+
return None
|
|
105
|
+
else:
|
|
106
|
+
# Load from file path
|
|
107
|
+
index_path = Path(custom_path)
|
|
108
|
+
if not index_path.exists():
|
|
109
|
+
logger.warning(f"Custom component index not found at {custom_path}")
|
|
110
|
+
return None
|
|
111
|
+
try:
|
|
112
|
+
blob = orjson.loads(index_path.read_bytes())
|
|
113
|
+
except orjson.JSONDecodeError as e:
|
|
114
|
+
logger.warning(f"Component index at {custom_path} is corrupted or invalid JSON: {e}")
|
|
115
|
+
return None
|
|
116
|
+
else:
|
|
117
|
+
# Use built-in index
|
|
118
|
+
pkg_dir = Path(inspect.getfile(lfx)).parent
|
|
119
|
+
index_path = pkg_dir / "_assets" / "component_index.json"
|
|
120
|
+
|
|
121
|
+
if not index_path.exists():
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
blob = orjson.loads(index_path.read_bytes())
|
|
126
|
+
except orjson.JSONDecodeError as e:
|
|
127
|
+
logger.warning(f"Built-in component index is corrupted or invalid JSON: {e}")
|
|
128
|
+
return None
|
|
129
|
+
|
|
130
|
+
# Integrity check: verify SHA256
|
|
131
|
+
tmp = dict(blob)
|
|
132
|
+
sha = tmp.pop("sha256", None)
|
|
133
|
+
if not sha:
|
|
134
|
+
logger.warning("Component index missing SHA256 hash - index may be tampered")
|
|
135
|
+
return None
|
|
136
|
+
|
|
137
|
+
# Use orjson for hash calculation to match build script
|
|
138
|
+
calc = hashlib.sha256(orjson.dumps(tmp, option=orjson.OPT_SORT_KEYS)).hexdigest()
|
|
139
|
+
if sha != calc:
|
|
140
|
+
logger.warning(
|
|
141
|
+
"Component index integrity check failed - SHA256 mismatch (file may be corrupted or tampered)"
|
|
142
|
+
)
|
|
143
|
+
return None
|
|
144
|
+
|
|
145
|
+
# Version check: ensure index matches installed langflow version
|
|
146
|
+
from importlib.metadata import version
|
|
147
|
+
|
|
148
|
+
installed_version = version("langflow")
|
|
149
|
+
if blob.get("version") != installed_version:
|
|
150
|
+
logger.debug(
|
|
151
|
+
f"Component index version mismatch: index={blob.get('version')}, installed={installed_version}"
|
|
152
|
+
)
|
|
153
|
+
return None
|
|
154
|
+
except Exception as e: # noqa: BLE001
|
|
155
|
+
logger.warning(f"Unexpected error reading component index: {type(e).__name__}: {e}")
|
|
156
|
+
return None
|
|
157
|
+
return blob
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _get_cache_path() -> Path:
|
|
161
|
+
"""Get the path for the cached component index in the user's cache directory."""
|
|
162
|
+
from platformdirs import user_cache_dir
|
|
163
|
+
|
|
164
|
+
cache_dir = Path(user_cache_dir("lfx", "langflow"))
|
|
165
|
+
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
166
|
+
return cache_dir / "component_index.json"
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _save_generated_index(modules_dict: dict) -> None:
|
|
170
|
+
"""Save a dynamically generated component index to cache for future use.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
modules_dict: Dictionary of components by category
|
|
174
|
+
"""
|
|
175
|
+
try:
|
|
176
|
+
cache_path = _get_cache_path()
|
|
177
|
+
|
|
178
|
+
# Convert modules_dict to entries format
|
|
179
|
+
entries = [[top_level, components] for top_level, components in modules_dict.items()]
|
|
180
|
+
|
|
181
|
+
# Calculate metadata
|
|
182
|
+
num_modules = len(modules_dict)
|
|
183
|
+
num_components = sum(len(components) for components in modules_dict.values())
|
|
184
|
+
|
|
185
|
+
# Get version
|
|
186
|
+
from importlib.metadata import version
|
|
187
|
+
|
|
188
|
+
langflow_version = version("langflow")
|
|
189
|
+
|
|
190
|
+
# Build index structure
|
|
191
|
+
index = {
|
|
192
|
+
"version": langflow_version,
|
|
193
|
+
"metadata": {
|
|
194
|
+
"num_modules": num_modules,
|
|
195
|
+
"num_components": num_components,
|
|
196
|
+
},
|
|
197
|
+
"entries": entries,
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
# Calculate hash
|
|
201
|
+
payload = orjson.dumps(index, option=orjson.OPT_SORT_KEYS)
|
|
202
|
+
index["sha256"] = hashlib.sha256(payload).hexdigest()
|
|
203
|
+
|
|
204
|
+
# Write to cache
|
|
205
|
+
json_bytes = orjson.dumps(index, option=orjson.OPT_SORT_KEYS | orjson.OPT_INDENT_2)
|
|
206
|
+
cache_path.write_bytes(json_bytes)
|
|
207
|
+
|
|
208
|
+
logger.debug(f"Saved generated component index to cache: {cache_path}")
|
|
209
|
+
except Exception as e: # noqa: BLE001
|
|
210
|
+
logger.debug(f"Failed to save generated index to cache: {e}")
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
async def _send_telemetry(
|
|
214
|
+
telemetry_service: Any,
|
|
215
|
+
index_source: str,
|
|
216
|
+
modules_dict: dict,
|
|
217
|
+
dev_mode: bool, # noqa: FBT001
|
|
218
|
+
target_modules: list[str] | None,
|
|
219
|
+
start_time_ms: int,
|
|
220
|
+
) -> None:
|
|
221
|
+
"""Send telemetry about component index loading.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
telemetry_service: Telemetry service instance (optional)
|
|
225
|
+
index_source: Source of the index ("builtin", "cache", or "dynamic")
|
|
226
|
+
modules_dict: Dictionary of loaded components
|
|
227
|
+
dev_mode: Whether dev mode is enabled
|
|
228
|
+
target_modules: List of filtered modules if any
|
|
229
|
+
start_time_ms: Start time in milliseconds
|
|
230
|
+
"""
|
|
231
|
+
if not telemetry_service:
|
|
232
|
+
return
|
|
233
|
+
|
|
234
|
+
try:
|
|
235
|
+
# Calculate metrics
|
|
236
|
+
num_modules = len(modules_dict)
|
|
237
|
+
num_components = sum(len(components) for components in modules_dict.values())
|
|
238
|
+
load_time_ms = int(time.time() * 1000) - start_time_ms
|
|
239
|
+
filtered_modules = ",".join(target_modules) if target_modules else None
|
|
240
|
+
|
|
241
|
+
# Import the payload class dynamically to avoid circular imports
|
|
242
|
+
from langflow.services.telemetry.schema import ComponentIndexPayload
|
|
243
|
+
|
|
244
|
+
payload = ComponentIndexPayload(
|
|
245
|
+
index_source=index_source,
|
|
246
|
+
num_modules=num_modules,
|
|
247
|
+
num_components=num_components,
|
|
248
|
+
dev_mode=dev_mode,
|
|
249
|
+
filtered_modules=filtered_modules,
|
|
250
|
+
load_time_ms=load_time_ms,
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
await telemetry_service.log_component_index(payload)
|
|
254
|
+
except Exception as e: # noqa: BLE001
|
|
255
|
+
# Don't fail component loading if telemetry fails
|
|
256
|
+
await logger.adebug(f"Failed to send component index telemetry: {e}")
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
async def _load_from_index_or_cache(
|
|
260
|
+
settings_service: Optional["SettingsService"] = None,
|
|
261
|
+
) -> tuple[dict[str, Any], str | None]:
|
|
262
|
+
"""Load components from prebuilt index or cache.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
settings_service: Optional settings service to get custom index path
|
|
266
|
+
|
|
267
|
+
Returns:
|
|
268
|
+
Tuple of (modules_dict, index_source) where index_source is "builtin", "cache", or None if failed
|
|
269
|
+
"""
|
|
270
|
+
modules_dict: dict[str, Any] = {}
|
|
271
|
+
|
|
272
|
+
# Try to load from prebuilt index first
|
|
273
|
+
custom_index_path = None
|
|
274
|
+
if settings_service and settings_service.settings.components_index_path:
|
|
275
|
+
custom_index_path = settings_service.settings.components_index_path
|
|
276
|
+
await logger.adebug(f"Using custom component index: {custom_index_path}")
|
|
277
|
+
|
|
278
|
+
index = _read_component_index(custom_index_path)
|
|
279
|
+
if index and "entries" in index:
|
|
280
|
+
source = custom_index_path or "built-in index"
|
|
281
|
+
await logger.adebug(f"Loading components from {source}")
|
|
282
|
+
# Reconstruct modules_dict from index entries
|
|
283
|
+
for top_level, components in index["entries"]:
|
|
284
|
+
if top_level not in modules_dict:
|
|
285
|
+
modules_dict[top_level] = {}
|
|
286
|
+
modules_dict[top_level].update(components)
|
|
287
|
+
await logger.adebug(f"Loaded {len(modules_dict)} component categories from index")
|
|
288
|
+
return modules_dict, "builtin"
|
|
289
|
+
|
|
290
|
+
# Index failed to load - try cache
|
|
291
|
+
await logger.adebug("Prebuilt index not available, checking cache")
|
|
292
|
+
try:
|
|
293
|
+
cache_path = _get_cache_path()
|
|
294
|
+
except Exception as e: # noqa: BLE001
|
|
295
|
+
await logger.adebug(f"Cache load failed: {e}")
|
|
296
|
+
else:
|
|
297
|
+
if cache_path.exists():
|
|
298
|
+
await logger.adebug(f"Attempting to load from cache: {cache_path}")
|
|
299
|
+
index = _read_component_index(str(cache_path))
|
|
300
|
+
if index and "entries" in index:
|
|
301
|
+
await logger.adebug("Loading components from cached index")
|
|
302
|
+
for top_level, components in index["entries"]:
|
|
303
|
+
if top_level not in modules_dict:
|
|
304
|
+
modules_dict[top_level] = {}
|
|
305
|
+
modules_dict[top_level].update(components)
|
|
306
|
+
await logger.adebug(f"Loaded {len(modules_dict)} component categories from cache")
|
|
307
|
+
return modules_dict, "cache"
|
|
308
|
+
|
|
309
|
+
return modules_dict, None
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
async def _load_components_dynamically(
|
|
313
|
+
target_modules: list[str] | None = None,
|
|
314
|
+
) -> dict[str, Any]:
|
|
315
|
+
"""Load components dynamically by scanning and importing modules.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
target_modules: Optional list of specific module names to load (e.g., ["mistral", "openai"])
|
|
319
|
+
|
|
320
|
+
Returns:
|
|
321
|
+
Dictionary mapping top-level module names to their components
|
|
322
|
+
"""
|
|
323
|
+
modules_dict: dict[str, Any] = {}
|
|
324
|
+
|
|
325
|
+
try:
|
|
326
|
+
import lfx.components as components_pkg
|
|
327
|
+
except ImportError as e:
|
|
328
|
+
await logger.aerror(f"Failed to import langflow.components package: {e}", exc_info=True)
|
|
329
|
+
return modules_dict
|
|
330
|
+
|
|
331
|
+
# Collect all module names to process
|
|
332
|
+
module_names = []
|
|
333
|
+
for _, modname, _ in pkgutil.walk_packages(components_pkg.__path__, prefix=components_pkg.__name__ + "."):
|
|
334
|
+
# Skip if the module is in the deactivated folder
|
|
335
|
+
if "deactivated" in modname:
|
|
336
|
+
continue
|
|
337
|
+
|
|
338
|
+
# If specific modules requested, filter by top-level module name
|
|
339
|
+
if target_modules:
|
|
340
|
+
# Extract top-level: "lfx.components.mistral.xyz" -> "mistral"
|
|
341
|
+
parts = modname.split(".")
|
|
342
|
+
if len(parts) > MIN_MODULE_PARTS and parts[2].lower() not in target_modules:
|
|
343
|
+
continue
|
|
344
|
+
|
|
345
|
+
module_names.append(modname)
|
|
346
|
+
|
|
347
|
+
if target_modules:
|
|
348
|
+
await logger.adebug(f"Found {len(module_names)} modules matching filter")
|
|
349
|
+
|
|
350
|
+
if not module_names:
|
|
351
|
+
return modules_dict
|
|
352
|
+
|
|
353
|
+
# Create tasks for parallel module processing
|
|
354
|
+
tasks = [asyncio.to_thread(_process_single_module, modname) for modname in module_names]
|
|
355
|
+
|
|
356
|
+
# Wait for all modules to be processed
|
|
357
|
+
try:
|
|
358
|
+
module_results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
359
|
+
except Exception as e: # noqa: BLE001
|
|
360
|
+
await logger.aerror(f"Error during parallel module processing: {e}", exc_info=True)
|
|
361
|
+
return modules_dict
|
|
362
|
+
|
|
363
|
+
# Merge results from all modules
|
|
364
|
+
for result in module_results:
|
|
365
|
+
if isinstance(result, Exception):
|
|
366
|
+
await logger.awarning(f"Module processing failed: {result}")
|
|
367
|
+
continue
|
|
368
|
+
|
|
369
|
+
if result and isinstance(result, tuple) and len(result) == EXPECTED_RESULT_LENGTH:
|
|
370
|
+
top_level, components = result
|
|
371
|
+
if top_level and components:
|
|
372
|
+
if top_level not in modules_dict:
|
|
373
|
+
modules_dict[top_level] = {}
|
|
374
|
+
modules_dict[top_level].update(components)
|
|
375
|
+
|
|
376
|
+
return modules_dict
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
async def _load_full_dev_mode() -> tuple[dict[str, Any], str]:
|
|
380
|
+
"""Load all components dynamically in full dev mode.
|
|
381
|
+
|
|
382
|
+
Returns:
|
|
383
|
+
Tuple of (modules_dict, index_source)
|
|
384
|
+
"""
|
|
385
|
+
await logger.adebug("LFX_DEV full mode: loading all modules dynamically")
|
|
386
|
+
modules_dict = await _load_components_dynamically(target_modules=None)
|
|
387
|
+
return modules_dict, "dynamic"
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
async def _load_selective_dev_mode(
|
|
391
|
+
settings_service: Optional["SettingsService"],
|
|
392
|
+
target_modules: list[str],
|
|
393
|
+
) -> tuple[dict[str, Any], str]:
|
|
394
|
+
"""Load index and selectively reload specific modules.
|
|
395
|
+
|
|
396
|
+
Args:
|
|
397
|
+
settings_service: Settings service for custom index path
|
|
398
|
+
target_modules: List of module names to reload
|
|
399
|
+
|
|
400
|
+
Returns:
|
|
401
|
+
Tuple of (modules_dict, index_source)
|
|
402
|
+
"""
|
|
403
|
+
await logger.adebug(f"LFX_DEV selective mode: reloading {target_modules}")
|
|
404
|
+
modules_dict, _ = await _load_from_index_or_cache(settings_service)
|
|
405
|
+
|
|
406
|
+
# Reload specific modules dynamically
|
|
407
|
+
dynamic_modules = await _load_components_dynamically(target_modules=target_modules)
|
|
408
|
+
|
|
409
|
+
# Merge/replace the targeted modules
|
|
410
|
+
for top_level, components in dynamic_modules.items():
|
|
411
|
+
if top_level not in modules_dict:
|
|
412
|
+
modules_dict[top_level] = {}
|
|
413
|
+
modules_dict[top_level].update(components)
|
|
414
|
+
|
|
415
|
+
await logger.adebug(f"Reloaded {len(target_modules)} module(s), kept others from index")
|
|
416
|
+
return modules_dict, "dynamic"
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
async def _load_production_mode(
|
|
420
|
+
settings_service: Optional["SettingsService"],
|
|
421
|
+
) -> tuple[dict[str, Any], str]:
|
|
422
|
+
"""Load components in production mode with fallback chain.
|
|
423
|
+
|
|
424
|
+
Tries: index -> cache -> dynamic build (with caching)
|
|
425
|
+
|
|
426
|
+
Args:
|
|
427
|
+
settings_service: Settings service for custom index path
|
|
428
|
+
|
|
429
|
+
Returns:
|
|
430
|
+
Tuple of (modules_dict, index_source)
|
|
431
|
+
"""
|
|
432
|
+
modules_dict, index_source = await _load_from_index_or_cache(settings_service)
|
|
433
|
+
|
|
434
|
+
if not index_source:
|
|
435
|
+
# No index or cache available - build dynamically and save
|
|
436
|
+
await logger.adebug("Falling back to dynamic loading")
|
|
437
|
+
modules_dict = await _load_components_dynamically(target_modules=None)
|
|
438
|
+
index_source = "dynamic"
|
|
439
|
+
|
|
440
|
+
# Save to cache for future use
|
|
441
|
+
if modules_dict:
|
|
442
|
+
await logger.adebug("Saving generated component index to cache")
|
|
443
|
+
_save_generated_index(modules_dict)
|
|
444
|
+
|
|
445
|
+
return modules_dict, index_source
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
async def import_langflow_components(
|
|
449
|
+
settings_service: Optional["SettingsService"] = None,
|
|
450
|
+
telemetry_service: Any | None = None,
|
|
451
|
+
) -> dict[str, dict[str, Any]]:
|
|
452
|
+
"""Asynchronously discovers and loads all built-in Langflow components.
|
|
453
|
+
|
|
454
|
+
Loading Strategy:
|
|
455
|
+
- Production mode: Load from prebuilt index -> cache -> build dynamically (with caching)
|
|
456
|
+
- Dev mode (full): Build all components dynamically
|
|
457
|
+
- Dev mode (selective): Load index + replace specific modules dynamically
|
|
458
|
+
|
|
459
|
+
Args:
|
|
460
|
+
settings_service: Optional settings service to get custom index path
|
|
461
|
+
telemetry_service: Optional telemetry service to log component loading metrics
|
|
462
|
+
|
|
463
|
+
Returns:
|
|
464
|
+
A dictionary with a "components" key mapping top-level package names to their component templates.
|
|
465
|
+
"""
|
|
466
|
+
start_time_ms: int = int(time.time() * 1000)
|
|
467
|
+
dev_mode_enabled, target_modules = _parse_dev_mode()
|
|
468
|
+
|
|
469
|
+
# Strategy pattern: map dev mode state to loading function
|
|
470
|
+
if dev_mode_enabled and not target_modules:
|
|
471
|
+
modules_dict, index_source = await _load_full_dev_mode()
|
|
472
|
+
elif dev_mode_enabled and target_modules:
|
|
473
|
+
modules_dict, index_source = await _load_selective_dev_mode(settings_service, target_modules)
|
|
474
|
+
else:
|
|
475
|
+
modules_dict, index_source = await _load_production_mode(settings_service)
|
|
476
|
+
|
|
477
|
+
# Send telemetry
|
|
478
|
+
await _send_telemetry(
|
|
479
|
+
telemetry_service, index_source, modules_dict, dev_mode_enabled, target_modules, start_time_ms
|
|
480
|
+
)
|
|
481
|
+
|
|
482
|
+
return {"components": modules_dict}
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def _process_single_module(modname: str) -> tuple[str, dict] | None:
|
|
486
|
+
"""Process a single module and return its components.
|
|
487
|
+
|
|
488
|
+
Args:
|
|
489
|
+
modname: The full module name to process
|
|
490
|
+
|
|
491
|
+
Returns:
|
|
492
|
+
A tuple of (top_level_package, components_dict) or None if processing failed
|
|
493
|
+
"""
|
|
494
|
+
try:
|
|
495
|
+
module = importlib.import_module(modname)
|
|
496
|
+
except Exception as e: # noqa: BLE001
|
|
497
|
+
# Catch all exceptions during import to prevent component failures from crashing startup
|
|
498
|
+
# TODO: Surface these errors to the UI in a friendly manner
|
|
499
|
+
logger.error(f"Failed to import module {modname}: {e}", exc_info=True)
|
|
500
|
+
return None
|
|
501
|
+
# Extract the top-level subpackage name after "lfx.components."
|
|
502
|
+
# e.g., "lfx.components.Notion.add_content_to_page" -> "Notion"
|
|
503
|
+
mod_parts = modname.split(".")
|
|
504
|
+
if len(mod_parts) <= MIN_MODULE_PARTS:
|
|
505
|
+
return None
|
|
506
|
+
|
|
507
|
+
top_level = mod_parts[2]
|
|
508
|
+
module_components = {}
|
|
509
|
+
|
|
510
|
+
# Bind frequently used functions for small speed gain
|
|
511
|
+
_getattr = getattr
|
|
512
|
+
|
|
513
|
+
# Fast path: only check class objects defined in this module
|
|
514
|
+
failed_count = []
|
|
515
|
+
for name, obj in vars(module).items():
|
|
516
|
+
if not isinstance(obj, type):
|
|
517
|
+
continue
|
|
518
|
+
|
|
519
|
+
# Only consider classes defined in this module
|
|
520
|
+
if obj.__module__ != modname:
|
|
521
|
+
continue
|
|
522
|
+
|
|
523
|
+
# Check for required attributes
|
|
524
|
+
if not (
|
|
525
|
+
_getattr(obj, "code_class_base_inheritance", None) is not None
|
|
526
|
+
or _getattr(obj, "_code_class_base_inheritance", None) is not None
|
|
527
|
+
):
|
|
528
|
+
continue
|
|
529
|
+
|
|
530
|
+
try:
|
|
531
|
+
comp_instance = obj()
|
|
532
|
+
# modname is the full module name without the name of the obj
|
|
533
|
+
full_module_name = f"{modname}.{name}"
|
|
534
|
+
comp_template, _ = create_component_template(
|
|
535
|
+
component_extractor=comp_instance, module_name=full_module_name
|
|
536
|
+
)
|
|
537
|
+
component_name = obj.name if hasattr(obj, "name") and obj.name else name
|
|
538
|
+
module_components[component_name] = comp_template
|
|
539
|
+
except Exception as e: # noqa: BLE001
|
|
540
|
+
failed_count.append(f"{name}: {e}")
|
|
541
|
+
continue
|
|
542
|
+
|
|
543
|
+
if failed_count:
|
|
544
|
+
logger.warning(
|
|
545
|
+
f"Skipped {len(failed_count)} component class{'es' if len(failed_count) != 1 else ''} "
|
|
546
|
+
f"in module '{modname}' due to instantiation failure: {', '.join(failed_count)}"
|
|
547
|
+
)
|
|
548
|
+
logger.debug(f"Processed module {modname}")
|
|
549
|
+
return (top_level, module_components)
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
async def _determine_loading_strategy(settings_service: "SettingsService") -> dict:
|
|
553
|
+
"""Determines and executes the appropriate component loading strategy.
|
|
554
|
+
|
|
555
|
+
Args:
|
|
556
|
+
settings_service: Service containing loading configuration
|
|
557
|
+
|
|
558
|
+
Returns:
|
|
559
|
+
Dictionary containing loaded component types and templates
|
|
560
|
+
"""
|
|
561
|
+
component_cache.all_types_dict = {}
|
|
562
|
+
if settings_service.settings.lazy_load_components:
|
|
563
|
+
# Partial loading mode - just load component metadata
|
|
564
|
+
await logger.adebug("Using partial component loading")
|
|
565
|
+
component_cache.all_types_dict = await aget_component_metadata(settings_service.settings.components_path)
|
|
566
|
+
elif settings_service.settings.components_path:
|
|
567
|
+
# Traditional full loading - filter out base components path to only load custom components
|
|
568
|
+
custom_paths = [p for p in settings_service.settings.components_path if p != BASE_COMPONENTS_PATH]
|
|
569
|
+
if custom_paths:
|
|
570
|
+
component_cache.all_types_dict = await aget_all_types_dict(custom_paths)
|
|
571
|
+
|
|
572
|
+
# Log custom component loading stats
|
|
573
|
+
components_dict = component_cache.all_types_dict or {}
|
|
574
|
+
component_count = sum(len(comps) for comps in components_dict.get("components", {}).values())
|
|
575
|
+
if component_count > 0 and settings_service.settings.components_path:
|
|
576
|
+
await logger.adebug(
|
|
577
|
+
f"Built {component_count} custom components from {settings_service.settings.components_path}"
|
|
578
|
+
)
|
|
579
|
+
|
|
580
|
+
return component_cache.all_types_dict
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
async def get_and_cache_all_types_dict(
|
|
584
|
+
settings_service: "SettingsService",
|
|
585
|
+
telemetry_service: Any | None = None,
|
|
586
|
+
):
|
|
587
|
+
"""Retrieves and caches the complete dictionary of component types and templates.
|
|
588
|
+
|
|
589
|
+
Supports both full and partial (lazy) loading. If the cache is empty, loads built-in Langflow
|
|
590
|
+
components and either fully loads all components or loads only their metadata, depending on the
|
|
591
|
+
lazy loading setting. Merges built-in and custom components into the cache and returns the
|
|
592
|
+
resulting dictionary.
|
|
593
|
+
|
|
594
|
+
Args:
|
|
595
|
+
settings_service: Settings service instance
|
|
596
|
+
telemetry_service: Optional telemetry service for tracking component loading metrics
|
|
597
|
+
"""
|
|
598
|
+
if component_cache.all_types_dict is None:
|
|
599
|
+
await logger.adebug("Building components cache")
|
|
600
|
+
|
|
601
|
+
langflow_components = await import_langflow_components(settings_service, telemetry_service)
|
|
602
|
+
custom_components_dict = await _determine_loading_strategy(settings_service)
|
|
603
|
+
|
|
604
|
+
# Flatten custom dict if it has a "components" wrapper
|
|
605
|
+
custom_flat = custom_components_dict.get("components", custom_components_dict) or {}
|
|
606
|
+
|
|
607
|
+
# Merge built-in and custom components (no wrapper at cache level)
|
|
608
|
+
component_cache.all_types_dict = {
|
|
609
|
+
**langflow_components["components"],
|
|
610
|
+
**custom_flat,
|
|
611
|
+
}
|
|
612
|
+
component_count = sum(len(comps) for comps in component_cache.all_types_dict.values())
|
|
613
|
+
await logger.adebug(f"Loaded {component_count} components")
|
|
614
|
+
return component_cache.all_types_dict
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
async def aget_all_types_dict(components_paths: list[str]):
|
|
618
|
+
"""Get all types dictionary with full component loading."""
|
|
619
|
+
return await abuild_custom_components(components_paths=components_paths)
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
async def aget_component_metadata(components_paths: list[str]):
|
|
623
|
+
"""Asynchronously retrieves minimal metadata for all components in the specified paths.
|
|
624
|
+
|
|
625
|
+
Builds a dictionary containing basic information (such as display name, type, and description) for
|
|
626
|
+
each discovered component, without loading their full templates. Each component entry is marked as
|
|
627
|
+
`lazy_loaded` to indicate that only metadata has been loaded.
|
|
628
|
+
|
|
629
|
+
Args:
|
|
630
|
+
components_paths: List of filesystem paths to search for component types and names.
|
|
631
|
+
|
|
632
|
+
Returns:
|
|
633
|
+
A dictionary with component types as keys and their corresponding component metadata as values.
|
|
634
|
+
"""
|
|
635
|
+
# This builds a skeleton of the all_types_dict with just basic component info
|
|
636
|
+
|
|
637
|
+
components_dict: dict = {"components": {}}
|
|
638
|
+
|
|
639
|
+
if not components_paths:
|
|
640
|
+
return components_dict
|
|
641
|
+
|
|
642
|
+
# Get all component types
|
|
643
|
+
component_types = await discover_component_types(components_paths)
|
|
644
|
+
await logger.adebug(f"Discovered {len(component_types)} component types: {', '.join(component_types)}")
|
|
645
|
+
|
|
646
|
+
# For each component type directory
|
|
647
|
+
for component_type in component_types:
|
|
648
|
+
components_dict["components"][component_type] = {}
|
|
649
|
+
|
|
650
|
+
# Get list of components in this type
|
|
651
|
+
component_names = await discover_component_names(component_type, components_paths)
|
|
652
|
+
await logger.adebug(f"Found {len(component_names)} components for type {component_type}")
|
|
653
|
+
|
|
654
|
+
# Create stub entries with just basic metadata
|
|
655
|
+
for name in component_names:
|
|
656
|
+
# Get minimal metadata for component
|
|
657
|
+
metadata = await get_component_minimal_metadata(component_type, name, components_paths)
|
|
658
|
+
|
|
659
|
+
if metadata:
|
|
660
|
+
components_dict["components"][component_type][name] = metadata
|
|
661
|
+
# Mark as needing full loading
|
|
662
|
+
components_dict["components"][component_type][name]["lazy_loaded"] = True
|
|
663
|
+
|
|
664
|
+
return components_dict
|
|
665
|
+
|
|
666
|
+
|
|
667
|
+
async def discover_component_types(components_paths: list[str]) -> list[str]:
|
|
668
|
+
"""Discover available component types by scanning directories."""
|
|
669
|
+
component_types: set[str] = set()
|
|
670
|
+
|
|
671
|
+
for path in components_paths:
|
|
672
|
+
path_obj = Path(path)
|
|
673
|
+
if not path_obj.exists():
|
|
674
|
+
continue
|
|
675
|
+
|
|
676
|
+
for item in path_obj.iterdir():
|
|
677
|
+
# Only include directories that don't start with _ or .
|
|
678
|
+
if item.is_dir() and not item.name.startswith(("_", ".")):
|
|
679
|
+
component_types.add(item.name)
|
|
680
|
+
|
|
681
|
+
# Add known types that might not be in directories
|
|
682
|
+
standard_types = {
|
|
683
|
+
"agents",
|
|
684
|
+
"chains",
|
|
685
|
+
"embeddings",
|
|
686
|
+
"llms",
|
|
687
|
+
"memories",
|
|
688
|
+
"prompts",
|
|
689
|
+
"tools",
|
|
690
|
+
"retrievers",
|
|
691
|
+
"textsplitters",
|
|
692
|
+
"toolkits",
|
|
693
|
+
"utilities",
|
|
694
|
+
"vectorstores",
|
|
695
|
+
"custom_components",
|
|
696
|
+
"documentloaders",
|
|
697
|
+
"outputparsers",
|
|
698
|
+
"wrappers",
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
component_types.update(standard_types)
|
|
702
|
+
|
|
703
|
+
return sorted(component_types)
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
async def discover_component_names(component_type: str, components_paths: list[str]) -> list[str]:
|
|
707
|
+
"""Discover component names for a specific type by scanning directories."""
|
|
708
|
+
component_names: set[str] = set()
|
|
709
|
+
|
|
710
|
+
for path in components_paths:
|
|
711
|
+
type_dir = Path(path) / component_type
|
|
712
|
+
|
|
713
|
+
if type_dir.exists():
|
|
714
|
+
for filename in type_dir.iterdir():
|
|
715
|
+
# Get Python files that don't start with __
|
|
716
|
+
if filename.name.endswith(".py") and not filename.name.startswith("__"):
|
|
717
|
+
component_name = filename.name[:-3] # Remove .py extension
|
|
718
|
+
component_names.add(component_name)
|
|
719
|
+
|
|
720
|
+
return sorted(component_names)
|
|
721
|
+
|
|
722
|
+
|
|
723
|
+
async def get_component_minimal_metadata(component_type: str, component_name: str, components_paths: list[str]):
|
|
724
|
+
"""Extract minimal metadata for a component without loading its full implementation."""
|
|
725
|
+
# Create a more complete metadata structure that the UI needs
|
|
726
|
+
metadata = {
|
|
727
|
+
"display_name": component_name.replace("_", " ").title(),
|
|
728
|
+
"name": component_name,
|
|
729
|
+
"type": component_type,
|
|
730
|
+
"description": f"A {component_type} component (not fully loaded)",
|
|
731
|
+
"template": {
|
|
732
|
+
"_type": component_type,
|
|
733
|
+
"inputs": {},
|
|
734
|
+
"outputs": {},
|
|
735
|
+
"output_types": [],
|
|
736
|
+
"documentation": f"A {component_type} component",
|
|
737
|
+
"display_name": component_name.replace("_", " ").title(),
|
|
738
|
+
"base_classes": [component_type],
|
|
739
|
+
},
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
# Try to find the file to verify it exists
|
|
743
|
+
component_path = None
|
|
744
|
+
for path in components_paths:
|
|
745
|
+
candidate_path = Path(path) / component_type / f"{component_name}.py"
|
|
746
|
+
if candidate_path.exists():
|
|
747
|
+
component_path = candidate_path
|
|
748
|
+
break
|
|
749
|
+
|
|
750
|
+
if not component_path:
|
|
751
|
+
return None
|
|
752
|
+
|
|
753
|
+
return metadata
|
|
754
|
+
|
|
755
|
+
|
|
756
|
+
async def ensure_component_loaded(component_type: str, component_name: str, settings_service: "SettingsService"):
|
|
757
|
+
"""Ensure a component is fully loaded if it was only partially loaded."""
|
|
758
|
+
# If already fully loaded, return immediately
|
|
759
|
+
component_key = f"{component_type}:{component_name}"
|
|
760
|
+
if component_key in component_cache.fully_loaded_components:
|
|
761
|
+
return
|
|
762
|
+
|
|
763
|
+
# If we don't have a cache or the component doesn't exist in the cache, nothing to do
|
|
764
|
+
if (
|
|
765
|
+
not component_cache.all_types_dict
|
|
766
|
+
or "components" not in component_cache.all_types_dict
|
|
767
|
+
or component_type not in component_cache.all_types_dict["components"]
|
|
768
|
+
or component_name not in component_cache.all_types_dict["components"][component_type]
|
|
769
|
+
):
|
|
770
|
+
return
|
|
771
|
+
|
|
772
|
+
# Check if component is marked for lazy loading
|
|
773
|
+
if component_cache.all_types_dict["components"][component_type][component_name].get("lazy_loaded", False):
|
|
774
|
+
await logger.adebug(f"Fully loading component {component_type}:{component_name}")
|
|
775
|
+
|
|
776
|
+
# Load just this specific component
|
|
777
|
+
full_component = await load_single_component(
|
|
778
|
+
component_type, component_name, settings_service.settings.components_path
|
|
779
|
+
)
|
|
780
|
+
|
|
781
|
+
if full_component:
|
|
782
|
+
# Replace the stub with the fully loaded component
|
|
783
|
+
component_cache.all_types_dict["components"][component_type][component_name] = full_component
|
|
784
|
+
# Remove lazy_loaded flag if it exists
|
|
785
|
+
if "lazy_loaded" in component_cache.all_types_dict["components"][component_type][component_name]:
|
|
786
|
+
del component_cache.all_types_dict["components"][component_type][component_name]["lazy_loaded"]
|
|
787
|
+
|
|
788
|
+
# Mark as fully loaded
|
|
789
|
+
component_cache.fully_loaded_components[component_key] = True
|
|
790
|
+
await logger.adebug(f"Component {component_type}:{component_name} fully loaded")
|
|
791
|
+
else:
|
|
792
|
+
await logger.awarning(f"Failed to fully load component {component_type}:{component_name}")
|
|
793
|
+
|
|
794
|
+
|
|
795
|
+
async def load_single_component(component_type: str, component_name: str, components_paths: list[str]):
|
|
796
|
+
"""Load a single component fully."""
|
|
797
|
+
from lfx.custom.utils import get_single_component_dict
|
|
798
|
+
|
|
799
|
+
try:
|
|
800
|
+
# Delegate to a more specific function that knows how to load
|
|
801
|
+
# a single component of a specific type
|
|
802
|
+
return await get_single_component_dict(component_type, component_name, components_paths)
|
|
803
|
+
except (ImportError, ModuleNotFoundError) as e:
|
|
804
|
+
# Handle issues with importing the component or its dependencies
|
|
805
|
+
await logger.aerror(f"Import error loading component {component_type}:{component_name}: {e!s}")
|
|
806
|
+
return None
|
|
807
|
+
except (AttributeError, TypeError) as e:
|
|
808
|
+
# Handle issues with component structure or type errors
|
|
809
|
+
await logger.aerror(f"Component structure error for {component_type}:{component_name}: {e!s}")
|
|
810
|
+
return None
|
|
811
|
+
except FileNotFoundError as e:
|
|
812
|
+
# Handle missing files
|
|
813
|
+
await logger.aerror(f"File not found for component {component_type}:{component_name}: {e!s}")
|
|
814
|
+
return None
|
|
815
|
+
except ValueError as e:
|
|
816
|
+
# Handle invalid values or configurations
|
|
817
|
+
await logger.aerror(f"Invalid configuration for component {component_type}:{component_name}: {e!s}")
|
|
818
|
+
return None
|
|
819
|
+
except (KeyError, IndexError) as e:
|
|
820
|
+
# Handle data structure access errors
|
|
821
|
+
await logger.aerror(f"Data structure error for component {component_type}:{component_name}: {e!s}")
|
|
822
|
+
return None
|
|
823
|
+
except RuntimeError as e:
|
|
824
|
+
# Handle runtime errors
|
|
825
|
+
await logger.aerror(f"Runtime error loading component {component_type}:{component_name}: {e!s}")
|
|
826
|
+
await logger.adebug("Full traceback for runtime error", exc_info=True)
|
|
827
|
+
return None
|
|
828
|
+
except OSError as e:
|
|
829
|
+
# Handle OS-related errors (file system, permissions, etc.)
|
|
830
|
+
await logger.aerror(f"OS error loading component {component_type}:{component_name}: {e!s}")
|
|
831
|
+
return None
|
|
832
|
+
|
|
833
|
+
|
|
834
|
+
# Also add a utility function to load specific component types
|
|
835
|
+
async def get_type_dict(component_type: str, settings_service: Optional["SettingsService"] = None):
|
|
836
|
+
"""Get a specific component type dictionary, loading if needed."""
|
|
837
|
+
if settings_service is None:
|
|
838
|
+
# Import here to avoid circular imports
|
|
839
|
+
from langflow.services.deps import get_settings_service
|
|
840
|
+
|
|
841
|
+
settings_service = get_settings_service()
|
|
842
|
+
|
|
843
|
+
# Make sure all_types_dict is loaded (at least partially)
|
|
844
|
+
if component_cache.all_types_dict is None:
|
|
845
|
+
await get_and_cache_all_types_dict(settings_service)
|
|
846
|
+
|
|
847
|
+
# Check if component type exists in the cache
|
|
848
|
+
if (
|
|
849
|
+
component_cache.all_types_dict
|
|
850
|
+
and "components" in component_cache.all_types_dict
|
|
851
|
+
and component_type in component_cache.all_types_dict["components"]
|
|
852
|
+
):
|
|
853
|
+
# If in lazy mode, ensure all components of this type are fully loaded
|
|
854
|
+
if settings_service.settings.lazy_load_components:
|
|
855
|
+
for component_name in list(component_cache.all_types_dict["components"][component_type].keys()):
|
|
856
|
+
await ensure_component_loaded(component_type, component_name, settings_service)
|
|
857
|
+
|
|
858
|
+
return component_cache.all_types_dict["components"][component_type]
|
|
859
|
+
|
|
860
|
+
return {}
|
|
861
|
+
|
|
862
|
+
|
|
863
|
+
# TypeError: unhashable type: 'list'
|
|
864
|
+
def key_func(*args, **kwargs):
|
|
865
|
+
# components_paths is a list of paths
|
|
866
|
+
return json.dumps(args) + json.dumps(kwargs)
|
|
867
|
+
|
|
868
|
+
|
|
869
|
+
async def aget_all_components(components_paths, *, as_dict=False):
|
|
870
|
+
"""Get all components names combining native and custom components."""
|
|
871
|
+
all_types_dict = await aget_all_types_dict(components_paths)
|
|
872
|
+
components = {} if as_dict else []
|
|
873
|
+
for category in all_types_dict.values():
|
|
874
|
+
for component in category.values():
|
|
875
|
+
component["name"] = component["display_name"]
|
|
876
|
+
if as_dict:
|
|
877
|
+
components[component["name"]] = component
|
|
878
|
+
else:
|
|
879
|
+
components.append(component)
|
|
880
|
+
return components
|
|
881
|
+
|
|
882
|
+
|
|
883
|
+
def get_all_components(components_paths, *, as_dict=False):
|
|
884
|
+
"""Get all components names combining native and custom components."""
|
|
885
|
+
# Import here to avoid circular imports
|
|
886
|
+
from lfx.custom.utils import build_custom_components
|
|
887
|
+
|
|
888
|
+
all_types_dict = build_custom_components(components_paths=components_paths)
|
|
889
|
+
components = [] if not as_dict else {}
|
|
890
|
+
for category in all_types_dict.values():
|
|
891
|
+
for component in category.values():
|
|
892
|
+
component["name"] = component["display_name"]
|
|
893
|
+
if as_dict:
|
|
894
|
+
components[component["name"]] = component
|
|
895
|
+
else:
|
|
896
|
+
components.append(component)
|
|
897
|
+
return components
|