lfx-nightly 0.1.11.dev0__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/__init__.py +0 -0
- lfx/__main__.py +25 -0
- lfx/base/__init__.py +0 -0
- lfx/base/agents/__init__.py +0 -0
- lfx/base/agents/agent.py +268 -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 +346 -0
- lfx/base/agents/utils.py +205 -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 +1291 -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 +685 -0
- lfx/base/data/docling_utils.py +245 -0
- lfx/base/data/utils.py +198 -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/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 +20 -0
- lfx/base/io/text.py +22 -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 +1398 -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 +47 -0
- lfx/base/models/aws_constants.py +151 -0
- lfx/base/models/chat_result.py +76 -0
- lfx/base/models/google_generative_ai_constants.py +70 -0
- lfx/base/models/groq_constants.py +134 -0
- lfx/base/models/model.py +375 -0
- lfx/base/models/model_input_constants.py +307 -0
- lfx/base/models/model_metadata.py +41 -0
- lfx/base/models/model_utils.py +8 -0
- lfx/base/models/novita_constants.py +35 -0
- lfx/base/models/ollama_constants.py +49 -0
- lfx/base/models/openai_constants.py +122 -0
- lfx/base/models/sambanova_constants.py +18 -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 +224 -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 +319 -0
- lfx/cli/common.py +650 -0
- lfx/cli/run.py +441 -0
- lfx/cli/script_loader.py +247 -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 +411 -0
- lfx/components/_importing.py +42 -0
- lfx/components/agentql/__init__.py +3 -0
- lfx/components/agentql/agentql_api.py +151 -0
- lfx/components/agents/__init__.py +34 -0
- lfx/components/agents/agent.py +558 -0
- lfx/components/agents/mcp_component.py +501 -0
- lfx/components/aiml/__init__.py +37 -0
- lfx/components/aiml/aiml.py +112 -0
- lfx/components/aiml/aiml_embeddings.py +37 -0
- lfx/components/amazon/__init__.py +36 -0
- lfx/components/amazon/amazon_bedrock_embedding.py +109 -0
- lfx/components/amazon/amazon_bedrock_model.py +124 -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 +163 -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 +167 -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/composio/__init__.py +74 -0
- lfx/components/composio/composio_api.py +268 -0
- lfx/components/composio/dropbox_compnent.py +11 -0
- lfx/components/composio/github_composio.py +11 -0
- lfx/components/composio/gmail_composio.py +38 -0
- lfx/components/composio/googlecalendar_composio.py +11 -0
- lfx/components/composio/googlemeet_composio.py +11 -0
- lfx/components/composio/googletasks_composio.py +8 -0
- lfx/components/composio/linear_composio.py +11 -0
- lfx/components/composio/outlook_composio.py +11 -0
- lfx/components/composio/reddit_composio.py +11 -0
- lfx/components/composio/slack_composio.py +582 -0
- lfx/components/composio/slackbot_composio.py +11 -0
- lfx/components/composio/supabase_composio.py +11 -0
- lfx/components/composio/todoist_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 +107 -0
- lfx/components/crewai/hierarchical_crew.py +46 -0
- lfx/components/crewai/hierarchical_task.py +44 -0
- lfx/components/crewai/sequential_crew.py +52 -0
- lfx/components/crewai/sequential_task.py +73 -0
- lfx/components/crewai/sequential_task_agent.py +143 -0
- lfx/components/custom_component/__init__.py +34 -0
- lfx/components/custom_component/custom_component.py +31 -0
- lfx/components/data/__init__.py +64 -0
- lfx/components/data/api_request.py +544 -0
- lfx/components/data/csv_to_data.py +95 -0
- lfx/components/data/directory.py +113 -0
- lfx/components/data/file.py +577 -0
- lfx/components/data/json_to_data.py +98 -0
- lfx/components/data/news_search.py +164 -0
- lfx/components/data/rss.py +69 -0
- lfx/components/data/sql_executor.py +101 -0
- lfx/components/data/url.py +311 -0
- lfx/components/data/web_search.py +112 -0
- lfx/components/data/webhook.py +56 -0
- lfx/components/datastax/__init__.py +70 -0
- lfx/components/datastax/astra_assistant_manager.py +306 -0
- lfx/components/datastax/astra_db.py +75 -0
- lfx/components/datastax/astra_vectorize.py +124 -0
- lfx/components/datastax/astradb.py +1285 -0
- lfx/components/datastax/astradb_cql.py +314 -0
- lfx/components/datastax/astradb_graph.py +330 -0
- lfx/components/datastax/astradb_tool.py +414 -0
- lfx/components/datastax/astradb_vectorstore.py +1285 -0
- lfx/components/datastax/cassandra.py +92 -0
- lfx/components/datastax/create_assistant.py +58 -0
- lfx/components/datastax/create_thread.py +32 -0
- lfx/components/datastax/dotenv.py +35 -0
- lfx/components/datastax/get_assistant.py +37 -0
- lfx/components/datastax/getenvvar.py +30 -0
- lfx/components/datastax/graph_rag.py +141 -0
- lfx/components/datastax/hcd.py +314 -0
- lfx/components/datastax/list_assistants.py +25 -0
- lfx/components/datastax/run.py +89 -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 +231 -0
- lfx/components/docling/docling_remote.py +193 -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 +243 -0
- lfx/components/embeddings/__init__.py +37 -0
- lfx/components/embeddings/similarity.py +76 -0
- lfx/components/embeddings/text_embedder.py +64 -0
- lfx/components/exa/__init__.py +3 -0
- lfx/components/exa/exa_search.py +68 -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/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 +192 -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 +147 -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 +136 -0
- lfx/components/helpers/__init__.py +52 -0
- lfx/components/helpers/calculator_core.py +89 -0
- lfx/components/helpers/create_list.py +40 -0
- lfx/components/helpers/current_date.py +42 -0
- lfx/components/helpers/id_generator.py +42 -0
- lfx/components/helpers/memory.py +251 -0
- lfx/components/helpers/output_parser.py +45 -0
- lfx/components/helpers/store_message.py +90 -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 +197 -0
- lfx/components/huggingface/huggingface_inference_api.py +106 -0
- lfx/components/ibm/__init__.py +34 -0
- lfx/components/ibm/watsonx.py +203 -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 +38 -0
- lfx/components/input_output/chat.py +120 -0
- lfx/components/input_output/chat_output.py +200 -0
- lfx/components/input_output/text.py +27 -0
- lfx/components/input_output/text_output.py +29 -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/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 +107 -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 +45 -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/lmstudio/__init__.py +34 -0
- lfx/components/lmstudio/lmstudioembeddings.py +89 -0
- lfx/components/lmstudio/lmstudiomodel.py +129 -0
- lfx/components/logic/__init__.py +52 -0
- lfx/components/logic/conditional_router.py +171 -0
- lfx/components/logic/data_conditional_router.py +125 -0
- lfx/components/logic/flow_tool.py +110 -0
- lfx/components/logic/listen.py +29 -0
- lfx/components/logic/loop.py +125 -0
- lfx/components/logic/notify.py +88 -0
- lfx/components/logic/pass_message.py +35 -0
- lfx/components/logic/run_flow.py +71 -0
- lfx/components/logic/sub_flow.py +114 -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 +136 -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 +34 -0
- lfx/components/models/embedding_model.py +114 -0
- lfx/components/models/language_model.py +144 -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 +157 -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 +330 -0
- lfx/components/ollama/ollama_embeddings.py +106 -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 +202 -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 +117 -0
- lfx/components/processing/alter_metadata.py +108 -0
- lfx/components/processing/batch_run.py +205 -0
- lfx/components/processing/combine_text.py +39 -0
- lfx/components/processing/converter.py +159 -0
- lfx/components/processing/create_data.py +110 -0
- lfx/components/processing/data_operations.py +438 -0
- lfx/components/processing/data_to_dataframe.py +70 -0
- lfx/components/processing/dataframe_operations.py +313 -0
- lfx/components/processing/extract_key.py +53 -0
- lfx/components/processing/filter_data.py +42 -0
- lfx/components/processing/filter_data_values.py +88 -0
- lfx/components/processing/json_cleaner.py +103 -0
- lfx/components/processing/lambda_filter.py +154 -0
- lfx/components/processing/llm_router.py +499 -0
- lfx/components/processing/merge_data.py +90 -0
- lfx/components/processing/message_to_data.py +36 -0
- lfx/components/processing/parse_data.py +70 -0
- lfx/components/processing/parse_dataframe.py +68 -0
- lfx/components/processing/parse_json_data.py +90 -0
- lfx/components/processing/parser.py +143 -0
- lfx/components/processing/prompt.py +67 -0
- lfx/components/processing/python_repl_core.py +98 -0
- lfx/components/processing/regex.py +82 -0
- lfx/components/processing/save_file.py +225 -0
- lfx/components/processing/select_data.py +48 -0
- lfx/components/processing/split_text.py +141 -0
- lfx/components/processing/structured_output.py +202 -0
- lfx/components/processing/update_data.py +160 -0
- lfx/components/prototypes/__init__.py +34 -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 +72 -0
- lfx/components/tools/calculator.py +108 -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 +327 -0
- lfx/components/tools/python_repl.py +97 -0
- lfx/components/tools/search_api.py +87 -0
- lfx/components/tools/searxng.py +145 -0
- lfx/components/tools/serp_api.py +119 -0
- lfx/components/tools/tavily_search_tool.py +344 -0
- lfx/components/tools/wikidata_api.py +102 -0
- lfx/components/tools/wikipedia_api.py +49 -0
- lfx/components/tools/yahoo_finance.py +129 -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 +291 -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 +179 -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/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 +40 -0
- lfx/components/vectorstores/astradb.py +1285 -0
- lfx/components/vectorstores/astradb_graph.py +319 -0
- lfx/components/vectorstores/cassandra.py +264 -0
- lfx/components/vectorstores/cassandra_graph.py +238 -0
- lfx/components/vectorstores/chroma.py +167 -0
- lfx/components/vectorstores/clickhouse.py +135 -0
- lfx/components/vectorstores/couchbase.py +102 -0
- lfx/components/vectorstores/elasticsearch.py +267 -0
- lfx/components/vectorstores/faiss.py +111 -0
- lfx/components/vectorstores/graph_rag.py +141 -0
- lfx/components/vectorstores/hcd.py +314 -0
- lfx/components/vectorstores/local_db.py +261 -0
- lfx/components/vectorstores/milvus.py +115 -0
- lfx/components/vectorstores/mongodb_atlas.py +213 -0
- lfx/components/vectorstores/opensearch.py +243 -0
- lfx/components/vectorstores/pgvector.py +72 -0
- lfx/components/vectorstores/pinecone.py +134 -0
- lfx/components/vectorstores/qdrant.py +109 -0
- lfx/components/vectorstores/supabase.py +76 -0
- lfx/components/vectorstores/upstash.py +124 -0
- lfx/components/vectorstores/vectara.py +97 -0
- lfx/components/vectorstores/vectara_rag.py +164 -0
- lfx/components/vectorstores/weaviate.py +89 -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/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 +118 -0
- lfx/components/zep/__init__.py +3 -0
- lfx/components/zep/zep.py +44 -0
- lfx/constants.py +6 -0
- lfx/custom/__init__.py +7 -0
- lfx/custom/attributes.py +86 -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 +1808 -0
- lfx/custom/custom_component/component_with_cache.py +8 -0
- lfx/custom/custom_component/custom_component.py +588 -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 +488 -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 +215 -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 +277 -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 +2238 -0
- lfx/graph/graph/constants.py +63 -0
- lfx/graph/graph/runnable_vertices_manager.py +133 -0
- lfx/graph/graph/schema.py +52 -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 +237 -0
- lfx/graph/utils.py +200 -0
- lfx/graph/vertex/__init__.py +0 -0
- lfx/graph/vertex/base.py +823 -0
- lfx/graph/vertex/constants.py +0 -0
- lfx/graph/vertex/exceptions.py +4 -0
- lfx/graph/vertex/param_handler.py +264 -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 +1 -0
- lfx/helpers/base_model.py +71 -0
- lfx/helpers/custom.py +13 -0
- lfx/helpers/data.py +167 -0
- lfx/helpers/flow.py +194 -0
- lfx/inputs/__init__.py +68 -0
- lfx/inputs/constants.py +2 -0
- lfx/inputs/input_mixin.py +328 -0
- lfx/inputs/inputs.py +714 -0
- lfx/inputs/validators.py +19 -0
- lfx/interface/__init__.py +6 -0
- lfx/interface/components.py +489 -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 +224 -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 +289 -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 +385 -0
- lfx/memory/__init__.py +90 -0
- lfx/memory/stubs.py +283 -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/data.py +308 -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 +131 -0
- lfx/schema/json_schema.py +141 -0
- lfx/schema/log.py +61 -0
- lfx/schema/message.py +473 -0
- lfx/schema/openai_responses_schemas.py +74 -0
- lfx/schema/properties.py +41 -0
- lfx/schema/schema.py +171 -0
- lfx/schema/serialize.py +13 -0
- lfx/schema/table.py +140 -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 +23 -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/deps.py +129 -0
- lfx/services/factory.py +19 -0
- lfx/services/initialize.py +19 -0
- lfx/services/interfaces.py +103 -0
- lfx/services/manager.py +172 -0
- lfx/services/schema.py +20 -0
- lfx/services/session.py +82 -0
- lfx/services/settings/__init__.py +3 -0
- lfx/services/settings/auth.py +130 -0
- lfx/services/settings/base.py +539 -0
- lfx/services/settings/constants.py +31 -0
- lfx/services/settings/factory.py +23 -0
- lfx/services/settings/feature_flags.py +12 -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 +155 -0
- lfx/services/storage/service.py +54 -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 +257 -0
- lfx/template/field/prompt.py +15 -0
- lfx/template/frontend_node/__init__.py +6 -0
- lfx/template/frontend_node/base.py +212 -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 +205 -0
- lfx/utils/data_structure.py +212 -0
- lfx/utils/exceptions.py +22 -0
- lfx/utils/helpers.py +28 -0
- lfx/utils/image.py +73 -0
- lfx/utils/lazy_load.py +15 -0
- lfx/utils/request_utils.py +18 -0
- lfx/utils/schemas.py +139 -0
- lfx/utils/util.py +481 -0
- lfx/utils/util_strings.py +56 -0
- lfx/utils/version.py +24 -0
- lfx_nightly-0.1.11.dev0.dist-info/METADATA +293 -0
- lfx_nightly-0.1.11.dev0.dist-info/RECORD +699 -0
- lfx_nightly-0.1.11.dev0.dist-info/WHEEL +4 -0
- lfx_nightly-0.1.11.dev0.dist-info/entry_points.txt +2 -0
lfx/custom/utils.py
ADDED
@@ -0,0 +1,877 @@
|
|
1
|
+
# mypy: ignore-errors
|
2
|
+
from __future__ import annotations
|
3
|
+
|
4
|
+
import ast
|
5
|
+
import asyncio
|
6
|
+
import contextlib
|
7
|
+
import hashlib
|
8
|
+
import inspect
|
9
|
+
import re
|
10
|
+
import traceback
|
11
|
+
from pathlib import Path
|
12
|
+
from typing import TYPE_CHECKING, Any
|
13
|
+
|
14
|
+
from fastapi import HTTPException
|
15
|
+
from pydantic import BaseModel
|
16
|
+
|
17
|
+
from lfx.custom import validate
|
18
|
+
from lfx.custom.custom_component.component import Component
|
19
|
+
from lfx.custom.custom_component.custom_component import CustomComponent
|
20
|
+
from lfx.custom.dependency_analyzer import analyze_component_dependencies
|
21
|
+
from lfx.custom.directory_reader.utils import (
|
22
|
+
abuild_custom_component_list_from_path,
|
23
|
+
build_custom_component_list_from_path,
|
24
|
+
merge_nested_dicts_with_renaming,
|
25
|
+
)
|
26
|
+
from lfx.custom.eval import eval_custom_component_code
|
27
|
+
from lfx.custom.schema import MissingDefault
|
28
|
+
from lfx.field_typing.range_spec import RangeSpec
|
29
|
+
from lfx.helpers.custom import format_type
|
30
|
+
from lfx.log.logger import logger
|
31
|
+
from lfx.schema.dotdict import dotdict
|
32
|
+
from lfx.template.field.base import Input
|
33
|
+
from lfx.template.frontend_node.custom_components import ComponentFrontendNode, CustomComponentFrontendNode
|
34
|
+
from lfx.type_extraction.type_extraction import extract_inner_type
|
35
|
+
from lfx.utils.util import get_base_classes
|
36
|
+
|
37
|
+
if TYPE_CHECKING:
|
38
|
+
from uuid import UUID
|
39
|
+
|
40
|
+
from lfx.custom.custom_component.custom_component import CustomComponent
|
41
|
+
|
42
|
+
|
43
|
+
def _generate_code_hash(source_code: str, modname: str) -> str:
|
44
|
+
"""Generate a hash of the component source code.
|
45
|
+
|
46
|
+
Args:
|
47
|
+
source_code: The source code string
|
48
|
+
modname: The module name for context
|
49
|
+
|
50
|
+
Returns:
|
51
|
+
SHA256 hash of the source code
|
52
|
+
|
53
|
+
Raises:
|
54
|
+
ValueError: If source_code is empty or None
|
55
|
+
UnicodeEncodeError: If source_code cannot be encoded
|
56
|
+
TypeError: If source_code is not a string
|
57
|
+
"""
|
58
|
+
if not isinstance(source_code, str):
|
59
|
+
msg = "Source code must be a string"
|
60
|
+
raise TypeError(msg)
|
61
|
+
|
62
|
+
if not source_code:
|
63
|
+
msg = f"Empty source code for {modname}"
|
64
|
+
raise ValueError(msg)
|
65
|
+
|
66
|
+
# Generate SHA256 hash of the source code
|
67
|
+
return hashlib.sha256(source_code.encode("utf-8")).hexdigest()[:12] # First 12 chars for brevity
|
68
|
+
|
69
|
+
|
70
|
+
class UpdateBuildConfigError(Exception):
|
71
|
+
pass
|
72
|
+
|
73
|
+
|
74
|
+
def add_output_types(frontend_node: CustomComponentFrontendNode, return_types: list[str]) -> None:
|
75
|
+
"""Add output types to the frontend node."""
|
76
|
+
for return_type in return_types:
|
77
|
+
if return_type is None:
|
78
|
+
raise HTTPException(
|
79
|
+
status_code=400,
|
80
|
+
detail={
|
81
|
+
"error": ("Invalid return type. Please check your code and try again."),
|
82
|
+
"traceback": traceback.format_exc(),
|
83
|
+
},
|
84
|
+
)
|
85
|
+
if return_type is str:
|
86
|
+
return_type_ = "Text"
|
87
|
+
elif hasattr(return_type, "__name__"):
|
88
|
+
return_type_ = return_type.__name__
|
89
|
+
elif hasattr(return_type, "__class__"):
|
90
|
+
return_type_ = return_type.__class__.__name__
|
91
|
+
else:
|
92
|
+
return_type_ = str(return_type)
|
93
|
+
|
94
|
+
frontend_node.add_output_type(return_type_)
|
95
|
+
|
96
|
+
|
97
|
+
def reorder_fields(frontend_node: CustomComponentFrontendNode, field_order: list[str]) -> None:
|
98
|
+
"""Reorder fields in the frontend node based on the specified field_order."""
|
99
|
+
if not field_order:
|
100
|
+
return
|
101
|
+
|
102
|
+
# Create a dictionary for O(1) lookup time.
|
103
|
+
field_dict = {field.name: field for field in frontend_node.template.fields}
|
104
|
+
reordered_fields = [field_dict[name] for name in field_order if name in field_dict]
|
105
|
+
# Add any fields that are not in the field_order list
|
106
|
+
reordered_fields.extend(field for field in frontend_node.template.fields if field.name not in field_order)
|
107
|
+
frontend_node.template.fields = reordered_fields
|
108
|
+
frontend_node.field_order = field_order
|
109
|
+
|
110
|
+
|
111
|
+
def add_base_classes(frontend_node: CustomComponentFrontendNode, return_types: list[str]) -> None:
|
112
|
+
"""Add base classes to the frontend node."""
|
113
|
+
for return_type_instance in return_types:
|
114
|
+
if return_type_instance is None:
|
115
|
+
raise HTTPException(
|
116
|
+
status_code=400,
|
117
|
+
detail={
|
118
|
+
"error": ("Invalid return type. Please check your code and try again."),
|
119
|
+
"traceback": traceback.format_exc(),
|
120
|
+
},
|
121
|
+
)
|
122
|
+
|
123
|
+
base_classes = get_base_classes(return_type_instance)
|
124
|
+
if return_type_instance is str:
|
125
|
+
base_classes.append("Text")
|
126
|
+
|
127
|
+
for base_class in base_classes:
|
128
|
+
frontend_node.add_base_class(base_class)
|
129
|
+
|
130
|
+
|
131
|
+
def extract_type_from_optional(field_type):
|
132
|
+
"""Extract the type from a string formatted as "Optional[<type>]".
|
133
|
+
|
134
|
+
Parameters:
|
135
|
+
field_type (str): The string from which to extract the type.
|
136
|
+
|
137
|
+
Returns:
|
138
|
+
str: The extracted type, or an empty string if no type was found.
|
139
|
+
"""
|
140
|
+
if "optional" not in field_type.lower():
|
141
|
+
return field_type
|
142
|
+
match = re.search(r"\[(.*?)\]$", field_type)
|
143
|
+
return match[1] if match else field_type
|
144
|
+
|
145
|
+
|
146
|
+
def get_field_properties(extra_field):
|
147
|
+
"""Get the properties of an extra field."""
|
148
|
+
field_name = extra_field["name"]
|
149
|
+
field_type = extra_field.get("type", "str")
|
150
|
+
field_value = extra_field.get("default", "")
|
151
|
+
# a required field is a field that does not contain
|
152
|
+
# optional in field_type
|
153
|
+
# and a field that does not have a default value
|
154
|
+
field_required = "optional" not in field_type.lower() and isinstance(field_value, MissingDefault)
|
155
|
+
field_value = field_value if not isinstance(field_value, MissingDefault) else None
|
156
|
+
|
157
|
+
if not field_required:
|
158
|
+
field_type = extract_type_from_optional(field_type)
|
159
|
+
if field_value is not None:
|
160
|
+
with contextlib.suppress(Exception):
|
161
|
+
field_value = ast.literal_eval(field_value)
|
162
|
+
return field_name, field_type, field_value, field_required
|
163
|
+
|
164
|
+
|
165
|
+
def process_type(field_type: str):
|
166
|
+
if field_type.startswith(("list", "List")):
|
167
|
+
return extract_inner_type(field_type)
|
168
|
+
|
169
|
+
# field_type is a string can be Prompt or Code too
|
170
|
+
# so we just need to lower if it is the case
|
171
|
+
lowercase_type = field_type.lower()
|
172
|
+
if lowercase_type in {"prompt", "code"}:
|
173
|
+
return lowercase_type
|
174
|
+
return field_type
|
175
|
+
|
176
|
+
|
177
|
+
def add_new_custom_field(
|
178
|
+
*,
|
179
|
+
frontend_node: CustomComponentFrontendNode,
|
180
|
+
field_name: str,
|
181
|
+
field_type: str,
|
182
|
+
field_value: Any,
|
183
|
+
field_required: bool,
|
184
|
+
field_config: dict,
|
185
|
+
):
|
186
|
+
# Check field_config if any of the keys are in it
|
187
|
+
# if it is, update the value
|
188
|
+
display_name = field_config.pop("display_name", None)
|
189
|
+
if not field_type:
|
190
|
+
if "type" in field_config and field_config["type"] is not None:
|
191
|
+
field_type = field_config.pop("type")
|
192
|
+
elif "field_type" in field_config and field_config["field_type"] is not None:
|
193
|
+
field_type = field_config.pop("field_type")
|
194
|
+
field_contains_list = "list" in field_type.lower()
|
195
|
+
field_type = process_type(field_type)
|
196
|
+
field_value = field_config.pop("value", field_value)
|
197
|
+
field_advanced = field_config.pop("advanced", False)
|
198
|
+
|
199
|
+
if field_type == "Dict":
|
200
|
+
field_type = "dict"
|
201
|
+
|
202
|
+
if field_type == "bool" and field_value is None:
|
203
|
+
field_value = False
|
204
|
+
|
205
|
+
if field_type == "SecretStr":
|
206
|
+
field_config["password"] = True
|
207
|
+
field_config["load_from_db"] = True
|
208
|
+
field_config["input_types"] = ["Text"]
|
209
|
+
|
210
|
+
# If options is a list, then it's a dropdown or multiselect
|
211
|
+
# If options is None, then it's a list of strings
|
212
|
+
is_list = isinstance(field_config.get("options"), list)
|
213
|
+
field_config["is_list"] = is_list or field_config.get("list", False) or field_contains_list
|
214
|
+
|
215
|
+
if "name" in field_config:
|
216
|
+
logger.warning("The 'name' key in field_config is used to build the object and can't be changed.")
|
217
|
+
required = field_config.pop("required", field_required)
|
218
|
+
placeholder = field_config.pop("placeholder", "")
|
219
|
+
|
220
|
+
new_field = Input(
|
221
|
+
name=field_name,
|
222
|
+
field_type=field_type,
|
223
|
+
value=field_value,
|
224
|
+
show=True,
|
225
|
+
required=required,
|
226
|
+
advanced=field_advanced,
|
227
|
+
placeholder=placeholder,
|
228
|
+
display_name=display_name,
|
229
|
+
**sanitize_field_config(field_config),
|
230
|
+
)
|
231
|
+
frontend_node.template.upsert_field(field_name, new_field)
|
232
|
+
if isinstance(frontend_node.custom_fields, dict):
|
233
|
+
frontend_node.custom_fields[field_name] = None
|
234
|
+
|
235
|
+
return frontend_node
|
236
|
+
|
237
|
+
|
238
|
+
def add_extra_fields(frontend_node, field_config, function_args) -> None:
|
239
|
+
"""Add extra fields to the frontend node."""
|
240
|
+
if not function_args:
|
241
|
+
return
|
242
|
+
field_config_ = field_config.copy()
|
243
|
+
function_args_names = [arg["name"] for arg in function_args]
|
244
|
+
# If kwargs is in the function_args and not all field_config keys are in function_args
|
245
|
+
# then we need to add the extra fields
|
246
|
+
|
247
|
+
for extra_field in function_args:
|
248
|
+
if "name" not in extra_field or extra_field["name"] in {
|
249
|
+
"self",
|
250
|
+
"kwargs",
|
251
|
+
"args",
|
252
|
+
}:
|
253
|
+
continue
|
254
|
+
|
255
|
+
field_name, field_type, field_value, field_required = get_field_properties(extra_field)
|
256
|
+
config = field_config_.pop(field_name, {})
|
257
|
+
frontend_node = add_new_custom_field(
|
258
|
+
frontend_node=frontend_node,
|
259
|
+
field_name=field_name,
|
260
|
+
field_type=field_type,
|
261
|
+
field_value=field_value,
|
262
|
+
field_required=field_required,
|
263
|
+
field_config=config,
|
264
|
+
)
|
265
|
+
if "kwargs" in function_args_names and not all(key in function_args_names for key in field_config):
|
266
|
+
for field_name, config in field_config_.items():
|
267
|
+
if "name" not in config or field_name == "code":
|
268
|
+
continue
|
269
|
+
config_ = config.model_dump() if isinstance(config, BaseModel) else config
|
270
|
+
field_name_, field_type, field_value, field_required = get_field_properties(extra_field=config_)
|
271
|
+
frontend_node = add_new_custom_field(
|
272
|
+
frontend_node=frontend_node,
|
273
|
+
field_name=field_name_,
|
274
|
+
field_type=field_type,
|
275
|
+
field_value=field_value,
|
276
|
+
field_required=field_required,
|
277
|
+
field_config=config_,
|
278
|
+
)
|
279
|
+
|
280
|
+
|
281
|
+
def get_field_dict(field: Input | dict):
|
282
|
+
"""Get the field dictionary from a Input or a dict."""
|
283
|
+
if isinstance(field, Input):
|
284
|
+
return dotdict(field.model_dump(by_alias=True, exclude_none=True))
|
285
|
+
return field
|
286
|
+
|
287
|
+
|
288
|
+
def run_build_inputs(
|
289
|
+
custom_component: Component,
|
290
|
+
):
|
291
|
+
"""Run the build inputs of a custom component."""
|
292
|
+
try:
|
293
|
+
return custom_component.build_inputs()
|
294
|
+
# add_extra_fields(frontend_node, field_config, field_config.values())
|
295
|
+
except Exception as exc:
|
296
|
+
logger.exception("Error running build inputs")
|
297
|
+
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
298
|
+
|
299
|
+
|
300
|
+
def get_component_instance(custom_component: CustomComponent | Component, user_id: str | UUID | None = None):
|
301
|
+
"""Returns an instance of a custom component, evaluating its code if necessary.
|
302
|
+
|
303
|
+
If the input is already an instance of `Component` or `CustomComponent`, it is returned directly.
|
304
|
+
Otherwise, the function evaluates the component's code to create and return an instance. Raises an
|
305
|
+
HTTP 400 error if the code is missing, invalid, or instantiation fails.
|
306
|
+
"""
|
307
|
+
# Fast path: avoid repeated str comparisons
|
308
|
+
|
309
|
+
code = custom_component._code
|
310
|
+
if not isinstance(code, str):
|
311
|
+
# Only two failure cases: None, or other non-str
|
312
|
+
error = "Code is None" if code is None else "Invalid code type"
|
313
|
+
msg = f"Invalid type conversion: {error}. Please check your code and try again."
|
314
|
+
logger.error(msg)
|
315
|
+
raise HTTPException(status_code=400, detail={"error": msg})
|
316
|
+
|
317
|
+
# Only now, try to process expensive exception/log traceback only *if needed*
|
318
|
+
try:
|
319
|
+
custom_class = eval_custom_component_code(code)
|
320
|
+
except Exception as exc:
|
321
|
+
# Only generate traceback if an error occurs (save time on success)
|
322
|
+
tb = traceback.format_exc()
|
323
|
+
logger.error("Error while evaluating custom component code\n%s", tb)
|
324
|
+
raise HTTPException(
|
325
|
+
status_code=400,
|
326
|
+
detail={
|
327
|
+
"error": "Invalid type conversion. Please check your code and try again.",
|
328
|
+
"traceback": tb,
|
329
|
+
},
|
330
|
+
) from exc
|
331
|
+
|
332
|
+
try:
|
333
|
+
return custom_class(_user_id=user_id, _code=code)
|
334
|
+
except Exception as exc:
|
335
|
+
tb = traceback.format_exc()
|
336
|
+
logger.error("Error while instantiating custom component\n%s", tb)
|
337
|
+
# Only log inner traceback if present in 'detail'
|
338
|
+
detail_tb = getattr(exc, "detail", {}).get("traceback", None)
|
339
|
+
if detail_tb is not None:
|
340
|
+
logger.error(detail_tb)
|
341
|
+
raise
|
342
|
+
|
343
|
+
|
344
|
+
def is_a_preimported_component(custom_component: CustomComponent):
|
345
|
+
"""Check if the component is a preimported component."""
|
346
|
+
klass = type(custom_component)
|
347
|
+
# This avoids double type lookups, and may speed up the common-case short-circuit
|
348
|
+
return issubclass(klass, Component) and klass is not Component
|
349
|
+
|
350
|
+
|
351
|
+
def run_build_config(
|
352
|
+
custom_component: CustomComponent,
|
353
|
+
user_id: str | UUID | None = None,
|
354
|
+
) -> tuple[dict, CustomComponent]:
|
355
|
+
"""Builds the field configuration dictionary for a custom component.
|
356
|
+
|
357
|
+
If the input is an instance of a subclass of Component (excluding Component itself), returns its
|
358
|
+
build configuration and the instance. Otherwise, evaluates the component's code to create an instance,
|
359
|
+
calls its build_config method, and processes any RangeSpec objects in the configuration. Raises an
|
360
|
+
HTTP 400 error if the code is missing or invalid, or if instantiation or configuration building fails.
|
361
|
+
|
362
|
+
Returns:
|
363
|
+
A tuple containing the field configuration dictionary and the component instance.
|
364
|
+
"""
|
365
|
+
# Check if the instance's class is a subclass of Component (but not Component itself)
|
366
|
+
# If we have a Component that is a subclass of Component, that means
|
367
|
+
# we have imported it
|
368
|
+
# If not, it means the component was loaded through LANGFLOW_COMPONENTS_PATH
|
369
|
+
# and loaded from a file
|
370
|
+
if is_a_preimported_component(custom_component):
|
371
|
+
return custom_component.build_config(), custom_component
|
372
|
+
|
373
|
+
if custom_component._code is None:
|
374
|
+
error = "Code is None"
|
375
|
+
elif not isinstance(custom_component._code, str):
|
376
|
+
error = "Invalid code type"
|
377
|
+
else:
|
378
|
+
try:
|
379
|
+
custom_class = eval_custom_component_code(custom_component._code)
|
380
|
+
except Exception as exc:
|
381
|
+
logger.exception("Error while evaluating custom component code")
|
382
|
+
raise HTTPException(
|
383
|
+
status_code=400,
|
384
|
+
detail={
|
385
|
+
"error": ("Invalid type conversion. Please check your code and try again."),
|
386
|
+
"traceback": traceback.format_exc(),
|
387
|
+
},
|
388
|
+
) from exc
|
389
|
+
|
390
|
+
try:
|
391
|
+
custom_instance = custom_class(_user_id=user_id)
|
392
|
+
build_config: dict = custom_instance.build_config()
|
393
|
+
|
394
|
+
for field_name, field in build_config.copy().items():
|
395
|
+
# Allow user to build Input as well
|
396
|
+
# as a dict with the same keys as Input
|
397
|
+
field_dict = get_field_dict(field)
|
398
|
+
# Let's check if "rangeSpec" is a RangeSpec object
|
399
|
+
if "rangeSpec" in field_dict and isinstance(field_dict["rangeSpec"], RangeSpec):
|
400
|
+
field_dict["rangeSpec"] = field_dict["rangeSpec"].model_dump()
|
401
|
+
build_config[field_name] = field_dict
|
402
|
+
|
403
|
+
except Exception as exc:
|
404
|
+
logger.exception("Error while building field config")
|
405
|
+
if hasattr(exc, "detail") and "traceback" in exc.detail:
|
406
|
+
logger.error(exc.detail["traceback"])
|
407
|
+
raise
|
408
|
+
return build_config, custom_instance
|
409
|
+
|
410
|
+
msg = f"Invalid type conversion: {error}. Please check your code and try again."
|
411
|
+
logger.error(msg)
|
412
|
+
raise HTTPException(
|
413
|
+
status_code=400,
|
414
|
+
detail={"error": msg},
|
415
|
+
)
|
416
|
+
|
417
|
+
|
418
|
+
def add_code_field(frontend_node: CustomComponentFrontendNode, raw_code):
|
419
|
+
code_field = Input(
|
420
|
+
dynamic=True,
|
421
|
+
required=True,
|
422
|
+
placeholder="",
|
423
|
+
multiline=True,
|
424
|
+
value=raw_code,
|
425
|
+
password=False,
|
426
|
+
name="code",
|
427
|
+
advanced=True,
|
428
|
+
field_type="code",
|
429
|
+
is_list=False,
|
430
|
+
)
|
431
|
+
frontend_node.template.add_field(code_field)
|
432
|
+
|
433
|
+
return frontend_node
|
434
|
+
|
435
|
+
|
436
|
+
def add_code_field_to_build_config(build_config: dict, raw_code: str):
|
437
|
+
build_config["code"] = Input(
|
438
|
+
dynamic=True,
|
439
|
+
required=True,
|
440
|
+
placeholder="",
|
441
|
+
multiline=True,
|
442
|
+
value=raw_code,
|
443
|
+
password=False,
|
444
|
+
name="code",
|
445
|
+
advanced=True,
|
446
|
+
field_type="code",
|
447
|
+
is_list=False,
|
448
|
+
).model_dump()
|
449
|
+
return build_config
|
450
|
+
|
451
|
+
|
452
|
+
def get_module_name_from_display_name(display_name: str):
|
453
|
+
"""Get the module name from the display name."""
|
454
|
+
# Convert display name to snake_case for Python module name
|
455
|
+
# e.g., "Custom Component" -> "custom_component"
|
456
|
+
# Remove extra spaces and convert to lowercase
|
457
|
+
cleaned_name = re.sub(r"\s+", " ", display_name.strip())
|
458
|
+
# Replace spaces with underscores and convert to lowercase
|
459
|
+
module_name = cleaned_name.replace(" ", "_").lower()
|
460
|
+
# Remove any non-alphanumeric characters except underscores
|
461
|
+
return re.sub(r"[^a-z0-9_]", "", module_name)
|
462
|
+
|
463
|
+
|
464
|
+
def build_custom_component_template_from_inputs(
|
465
|
+
custom_component: Component | CustomComponent, user_id: str | UUID | None = None, module_name: str | None = None
|
466
|
+
):
|
467
|
+
# The List of Inputs fills the role of the build_config and the entrypoint_args
|
468
|
+
"""Builds a frontend node template from a custom component using its input-based configuration.
|
469
|
+
|
470
|
+
This function generates a frontend node template by extracting input fields from the component,
|
471
|
+
adding the code field, determining output types from method return types, validating the component,
|
472
|
+
setting base classes, and reordering fields. Returns the frontend node as a dictionary along with
|
473
|
+
the component instance.
|
474
|
+
|
475
|
+
Returns:
|
476
|
+
A tuple containing the frontend node dictionary and the component instance.
|
477
|
+
"""
|
478
|
+
ctype_name = custom_component.__class__.__name__
|
479
|
+
if ctype_name in _COMPONENT_TYPE_NAMES:
|
480
|
+
cc_instance = get_component_instance(custom_component, user_id=user_id)
|
481
|
+
|
482
|
+
field_config = cc_instance.get_template_config(cc_instance)
|
483
|
+
frontend_node = ComponentFrontendNode.from_inputs(**field_config)
|
484
|
+
|
485
|
+
else:
|
486
|
+
frontend_node = ComponentFrontendNode.from_inputs(**custom_component.template_config)
|
487
|
+
cc_instance = custom_component
|
488
|
+
frontend_node = add_code_field(frontend_node, custom_component._code)
|
489
|
+
# But we now need to calculate the return_type of the methods in the outputs
|
490
|
+
for output in frontend_node.outputs:
|
491
|
+
if output.types:
|
492
|
+
continue
|
493
|
+
return_types = cc_instance.get_method_return_type(output.method)
|
494
|
+
return_types = [format_type(return_type) for return_type in return_types]
|
495
|
+
output.add_types(return_types)
|
496
|
+
|
497
|
+
# Validate that there is not name overlap between inputs and outputs
|
498
|
+
frontend_node.validate_component()
|
499
|
+
# ! This should be removed when we have a better way to handle this
|
500
|
+
frontend_node.set_base_classes_from_outputs()
|
501
|
+
reorder_fields(frontend_node, cc_instance._get_field_order())
|
502
|
+
frontend_node = build_component_metadata(frontend_node, cc_instance, module_name, ctype_name)
|
503
|
+
|
504
|
+
return frontend_node.to_dict(keep_name=False), cc_instance
|
505
|
+
|
506
|
+
|
507
|
+
def build_component_metadata(
|
508
|
+
frontend_node: CustomComponentFrontendNode, custom_component: CustomComponent, module_name: str, ctype_name: str
|
509
|
+
):
|
510
|
+
"""Build the metadata for a custom component."""
|
511
|
+
if module_name:
|
512
|
+
frontend_node.metadata["module"] = module_name
|
513
|
+
else:
|
514
|
+
module_name = get_module_name_from_display_name(frontend_node.display_name)
|
515
|
+
frontend_node.metadata["module"] = f"custom_components.{module_name}"
|
516
|
+
|
517
|
+
# Generate code hash for cache invalidation and debugging
|
518
|
+
try:
|
519
|
+
code_hash = _generate_code_hash(custom_component._code, module_name)
|
520
|
+
if code_hash:
|
521
|
+
frontend_node.metadata["code_hash"] = code_hash
|
522
|
+
except Exception as exc: # noqa: BLE001
|
523
|
+
logger.debug(f"Error generating code hash for {custom_component.__class__.__name__}", exc_info=exc)
|
524
|
+
|
525
|
+
# Analyze component dependencies
|
526
|
+
try:
|
527
|
+
dependency_info = analyze_component_dependencies(custom_component._code)
|
528
|
+
frontend_node.metadata["dependencies"] = dependency_info
|
529
|
+
except (SyntaxError, TypeError, ValueError, ImportError) as exc:
|
530
|
+
logger.warning(f"Failed to analyze dependencies for component {ctype_name}: {exc}")
|
531
|
+
# Set minimal dependency info on failure
|
532
|
+
frontend_node.metadata["dependencies"] = {
|
533
|
+
"total_dependencies": 0,
|
534
|
+
"dependencies": [],
|
535
|
+
}
|
536
|
+
|
537
|
+
return frontend_node
|
538
|
+
|
539
|
+
|
540
|
+
def build_custom_component_template(
|
541
|
+
custom_component: CustomComponent,
|
542
|
+
user_id: str | UUID | None = None,
|
543
|
+
module_name: str | None = None,
|
544
|
+
) -> tuple[dict[str, Any], CustomComponent | Component]:
|
545
|
+
"""Builds a frontend node template and instance for a custom component.
|
546
|
+
|
547
|
+
If the component uses input-based configuration, delegates to the appropriate builder. Otherwise,
|
548
|
+
constructs a frontend node from the component's template configuration, adds extra fields, code,
|
549
|
+
base classes, and output types, reorders fields, and returns the resulting template dictionary
|
550
|
+
along with the component instance.
|
551
|
+
|
552
|
+
Raises:
|
553
|
+
HTTPException: If the component is missing required attributes or if any error occurs during
|
554
|
+
template construction.
|
555
|
+
"""
|
556
|
+
try:
|
557
|
+
has_template_config = hasattr(custom_component, "template_config")
|
558
|
+
except Exception as exc:
|
559
|
+
raise HTTPException(
|
560
|
+
status_code=400,
|
561
|
+
detail={
|
562
|
+
"error": (f"Error building Component: {exc}"),
|
563
|
+
"traceback": traceback.format_exc(),
|
564
|
+
},
|
565
|
+
) from exc
|
566
|
+
if not has_template_config:
|
567
|
+
raise HTTPException(
|
568
|
+
status_code=400,
|
569
|
+
detail={
|
570
|
+
"error": ("Error building Component. Please check if you are importing Component correctly."),
|
571
|
+
},
|
572
|
+
)
|
573
|
+
try:
|
574
|
+
if "inputs" in custom_component.template_config:
|
575
|
+
return build_custom_component_template_from_inputs(
|
576
|
+
custom_component, user_id=user_id, module_name=module_name
|
577
|
+
)
|
578
|
+
frontend_node = CustomComponentFrontendNode(**custom_component.template_config)
|
579
|
+
|
580
|
+
field_config, custom_instance = run_build_config(
|
581
|
+
custom_component,
|
582
|
+
user_id=user_id,
|
583
|
+
)
|
584
|
+
|
585
|
+
entrypoint_args = custom_component.get_function_entrypoint_args
|
586
|
+
|
587
|
+
add_extra_fields(frontend_node, field_config, entrypoint_args)
|
588
|
+
|
589
|
+
frontend_node = add_code_field(frontend_node, custom_component._code)
|
590
|
+
|
591
|
+
add_base_classes(frontend_node, custom_component._get_function_entrypoint_return_type)
|
592
|
+
add_output_types(frontend_node, custom_component._get_function_entrypoint_return_type)
|
593
|
+
|
594
|
+
reorder_fields(frontend_node, custom_instance._get_field_order())
|
595
|
+
|
596
|
+
if module_name:
|
597
|
+
frontend_node = build_component_metadata(
|
598
|
+
frontend_node, custom_component, module_name, custom_component.__class__.__name__
|
599
|
+
)
|
600
|
+
|
601
|
+
return frontend_node.to_dict(keep_name=False), custom_instance
|
602
|
+
except Exception as exc:
|
603
|
+
if isinstance(exc, HTTPException):
|
604
|
+
raise
|
605
|
+
raise HTTPException(
|
606
|
+
status_code=400,
|
607
|
+
detail={
|
608
|
+
"error": (f"Error building Component: {exc}"),
|
609
|
+
"traceback": traceback.format_exc(),
|
610
|
+
},
|
611
|
+
) from exc
|
612
|
+
|
613
|
+
|
614
|
+
def create_component_template(
|
615
|
+
component: dict | None = None,
|
616
|
+
component_extractor: Component | CustomComponent | None = None,
|
617
|
+
module_name: str | None = None,
|
618
|
+
):
|
619
|
+
"""Creates a component template and instance from either a component dictionary or an existing component extractor.
|
620
|
+
|
621
|
+
If a component dictionary is provided, a new Component instance is created from its code. If a component
|
622
|
+
extractor is provided, it is used directly. The function returns the generated template and the component
|
623
|
+
instance. Output types are set on the template if missing.
|
624
|
+
"""
|
625
|
+
component_output_types = []
|
626
|
+
if component_extractor is None and component is not None:
|
627
|
+
component_code = component["code"]
|
628
|
+
component_output_types = component["output_types"]
|
629
|
+
|
630
|
+
component_extractor = Component(_code=component_code)
|
631
|
+
|
632
|
+
component_template, component_instance = build_custom_component_template(
|
633
|
+
component_extractor, module_name=module_name
|
634
|
+
)
|
635
|
+
if not component_template["output_types"] and component_output_types:
|
636
|
+
component_template["output_types"] = component_output_types
|
637
|
+
|
638
|
+
return component_template, component_instance
|
639
|
+
|
640
|
+
|
641
|
+
def build_custom_components(components_paths: list[str]):
|
642
|
+
"""Build custom components from the specified paths."""
|
643
|
+
if not components_paths:
|
644
|
+
return {}
|
645
|
+
|
646
|
+
logger.info(f"Building custom components from {components_paths}")
|
647
|
+
|
648
|
+
custom_components_from_file: dict = {}
|
649
|
+
processed_paths = set()
|
650
|
+
for path in components_paths:
|
651
|
+
path_str = str(path)
|
652
|
+
if path_str in processed_paths:
|
653
|
+
continue
|
654
|
+
|
655
|
+
custom_component_dict = build_custom_component_list_from_path(path_str)
|
656
|
+
if custom_component_dict:
|
657
|
+
category = next(iter(custom_component_dict))
|
658
|
+
logger.debug(f"Loading {len(custom_component_dict[category])} component(s) from category {category}")
|
659
|
+
custom_components_from_file = merge_nested_dicts_with_renaming(
|
660
|
+
custom_components_from_file, custom_component_dict
|
661
|
+
)
|
662
|
+
processed_paths.add(path_str)
|
663
|
+
|
664
|
+
return custom_components_from_file
|
665
|
+
|
666
|
+
|
667
|
+
async def abuild_custom_components(components_paths: list[str]):
|
668
|
+
"""Build custom components from the specified paths."""
|
669
|
+
if not components_paths:
|
670
|
+
return {}
|
671
|
+
|
672
|
+
await logger.adebug(f"Building custom components from {components_paths}")
|
673
|
+
custom_components_from_file: dict = {}
|
674
|
+
processed_paths = set()
|
675
|
+
for path in components_paths:
|
676
|
+
path_str = str(path)
|
677
|
+
if path_str in processed_paths:
|
678
|
+
continue
|
679
|
+
|
680
|
+
custom_component_dict = await abuild_custom_component_list_from_path(path_str)
|
681
|
+
if custom_component_dict:
|
682
|
+
category = next(iter(custom_component_dict))
|
683
|
+
await logger.adebug(f"Loading {len(custom_component_dict[category])} component(s) from category {category}")
|
684
|
+
custom_components_from_file = merge_nested_dicts_with_renaming(
|
685
|
+
custom_components_from_file, custom_component_dict
|
686
|
+
)
|
687
|
+
processed_paths.add(path_str)
|
688
|
+
|
689
|
+
return custom_components_from_file
|
690
|
+
|
691
|
+
|
692
|
+
def sanitize_field_config(field_config: dict | Input):
|
693
|
+
# If any of the already existing keys are in field_config, remove them
|
694
|
+
field_dict = field_config.to_dict() if isinstance(field_config, Input) else field_config
|
695
|
+
for key in [
|
696
|
+
"name",
|
697
|
+
"field_type",
|
698
|
+
"value",
|
699
|
+
"required",
|
700
|
+
"placeholder",
|
701
|
+
"display_name",
|
702
|
+
"advanced",
|
703
|
+
"show",
|
704
|
+
]:
|
705
|
+
field_dict.pop(key, None)
|
706
|
+
|
707
|
+
# Remove field_type and type because they were extracted already
|
708
|
+
field_dict.pop("field_type", None)
|
709
|
+
field_dict.pop("type", None)
|
710
|
+
|
711
|
+
return field_dict
|
712
|
+
|
713
|
+
|
714
|
+
def build_component(component):
|
715
|
+
"""Build a single component."""
|
716
|
+
component_template, component_instance = create_component_template(component)
|
717
|
+
component_name = get_instance_name(component_instance)
|
718
|
+
return component_name, component_template
|
719
|
+
|
720
|
+
|
721
|
+
def get_function(code):
|
722
|
+
"""Get the function."""
|
723
|
+
function_name = validate.extract_function_name(code)
|
724
|
+
|
725
|
+
return validate.create_function(code, function_name)
|
726
|
+
|
727
|
+
|
728
|
+
def get_instance_name(instance):
|
729
|
+
name = instance.__class__.__name__
|
730
|
+
if hasattr(instance, "name") and instance.name:
|
731
|
+
name = instance.name
|
732
|
+
return name
|
733
|
+
|
734
|
+
|
735
|
+
async def update_component_build_config(
|
736
|
+
component: CustomComponent,
|
737
|
+
build_config: dotdict,
|
738
|
+
field_value: Any,
|
739
|
+
field_name: str | None = None,
|
740
|
+
):
|
741
|
+
if inspect.iscoroutinefunction(component.update_build_config):
|
742
|
+
return await component.update_build_config(build_config, field_value, field_name)
|
743
|
+
return await asyncio.to_thread(component.update_build_config, build_config, field_value, field_name)
|
744
|
+
|
745
|
+
|
746
|
+
async def get_all_types_dict(components_paths: list[str]):
|
747
|
+
"""Get all types dictionary with full component loading."""
|
748
|
+
# This is the async version of the existing function
|
749
|
+
return await abuild_custom_components(components_paths=components_paths)
|
750
|
+
|
751
|
+
|
752
|
+
async def get_single_component_dict(component_type: str, component_name: str, components_paths: list[str]):
|
753
|
+
"""Get a single component dictionary."""
|
754
|
+
# For example, if components are loaded by importing Python modules:
|
755
|
+
for base_path in components_paths:
|
756
|
+
module_path = Path(base_path) / component_type / f"{component_name}.py"
|
757
|
+
if module_path.exists():
|
758
|
+
# Try to import the module
|
759
|
+
module_name = f"lfx.components.{component_type}.{component_name}"
|
760
|
+
try:
|
761
|
+
# This is a simplified example - actual implementation may vary
|
762
|
+
import importlib.util
|
763
|
+
|
764
|
+
spec = importlib.util.spec_from_file_location(module_name, module_path)
|
765
|
+
if spec and spec.loader:
|
766
|
+
module = importlib.util.module_from_spec(spec)
|
767
|
+
spec.loader.exec_module(module)
|
768
|
+
if hasattr(module, "template"):
|
769
|
+
return module.template
|
770
|
+
except ImportError as e:
|
771
|
+
await logger.aerror(f"Import error loading component {module_path}: {e!s}")
|
772
|
+
except AttributeError as e:
|
773
|
+
await logger.aerror(f"Attribute error loading component {module_path}: {e!s}")
|
774
|
+
except ValueError as e:
|
775
|
+
await logger.aerror(f"Value error loading component {module_path}: {e!s}")
|
776
|
+
except (KeyError, IndexError) as e:
|
777
|
+
await logger.aerror(f"Data structure error loading component {module_path}: {e!s}")
|
778
|
+
except RuntimeError as e:
|
779
|
+
await logger.aerror(f"Runtime error loading component {module_path}: {e!s}")
|
780
|
+
await logger.adebug("Full traceback for runtime error", exc_info=True)
|
781
|
+
except OSError as e:
|
782
|
+
await logger.aerror(f"OS error loading component {module_path}: {e!s}")
|
783
|
+
|
784
|
+
# If we get here, the component wasn't found or couldn't be loaded
|
785
|
+
return None
|
786
|
+
|
787
|
+
|
788
|
+
async def load_custom_component(component_name: str, components_paths: list[str]):
|
789
|
+
"""Load a custom component by name.
|
790
|
+
|
791
|
+
Args:
|
792
|
+
component_name: Name of the component to load
|
793
|
+
components_paths: List of paths to search for components
|
794
|
+
"""
|
795
|
+
from lfx.interface.custom_component import get_custom_component_from_name
|
796
|
+
|
797
|
+
try:
|
798
|
+
# First try to get the component from the registered components
|
799
|
+
component_class = get_custom_component_from_name(component_name)
|
800
|
+
if component_class:
|
801
|
+
# Define the function locally if it's not imported
|
802
|
+
def get_custom_component_template(component_cls):
|
803
|
+
"""Get template for a custom component class."""
|
804
|
+
# This is a simplified implementation - adjust as needed
|
805
|
+
if hasattr(component_cls, "get_template"):
|
806
|
+
return component_cls.get_template()
|
807
|
+
if hasattr(component_cls, "template"):
|
808
|
+
return component_cls.template
|
809
|
+
return None
|
810
|
+
|
811
|
+
return get_custom_component_template(component_class)
|
812
|
+
|
813
|
+
# If not found in registered components, search in the provided paths
|
814
|
+
for path in components_paths:
|
815
|
+
# Try to find the component in different category directories
|
816
|
+
base_path = Path(path)
|
817
|
+
if base_path.exists() and base_path.is_dir():
|
818
|
+
# Search for the component in all subdirectories
|
819
|
+
for category_dir in base_path.iterdir():
|
820
|
+
if category_dir.is_dir():
|
821
|
+
component_file = category_dir / f"{component_name}.py"
|
822
|
+
if component_file.exists():
|
823
|
+
# Try to import the module
|
824
|
+
module_name = f"lfx.components.{category_dir.name}.{component_name}"
|
825
|
+
try:
|
826
|
+
import importlib.util
|
827
|
+
|
828
|
+
spec = importlib.util.spec_from_file_location(module_name, component_file)
|
829
|
+
if spec and spec.loader:
|
830
|
+
module = importlib.util.module_from_spec(spec)
|
831
|
+
spec.loader.exec_module(module)
|
832
|
+
if hasattr(module, "template"):
|
833
|
+
return module.template
|
834
|
+
if hasattr(module, "get_template"):
|
835
|
+
return module.get_template()
|
836
|
+
except ImportError as e:
|
837
|
+
await logger.aerror(f"Import error loading component {component_file}: {e!s}")
|
838
|
+
await logger.adebug("Import error traceback", exc_info=True)
|
839
|
+
except AttributeError as e:
|
840
|
+
await logger.aerror(f"Attribute error loading component {component_file}: {e!s}")
|
841
|
+
await logger.adebug("Attribute error traceback", exc_info=True)
|
842
|
+
except (ValueError, TypeError) as e:
|
843
|
+
await logger.aerror(f"Value/Type error loading component {component_file}: {e!s}")
|
844
|
+
await logger.adebug("Value/Type error traceback", exc_info=True)
|
845
|
+
except (KeyError, IndexError) as e:
|
846
|
+
await logger.aerror(f"Data structure error loading component {component_file}: {e!s}")
|
847
|
+
await logger.adebug("Data structure error traceback", exc_info=True)
|
848
|
+
except RuntimeError as e:
|
849
|
+
await logger.aerror(f"Runtime error loading component {component_file}: {e!s}")
|
850
|
+
await logger.adebug("Runtime error traceback", exc_info=True)
|
851
|
+
except OSError as e:
|
852
|
+
await logger.aerror(f"OS error loading component {component_file}: {e!s}")
|
853
|
+
await logger.adebug("OS error traceback", exc_info=True)
|
854
|
+
|
855
|
+
except ImportError as e:
|
856
|
+
await logger.aerror(f"Import error loading custom component {component_name}: {e!s}")
|
857
|
+
return None
|
858
|
+
except AttributeError as e:
|
859
|
+
await logger.aerror(f"Attribute error loading custom component {component_name}: {e!s}")
|
860
|
+
return None
|
861
|
+
except ValueError as e:
|
862
|
+
await logger.aerror(f"Value error loading custom component {component_name}: {e!s}")
|
863
|
+
return None
|
864
|
+
except (KeyError, IndexError) as e:
|
865
|
+
await logger.aerror(f"Data structure error loading custom component {component_name}: {e!s}")
|
866
|
+
return None
|
867
|
+
except RuntimeError as e:
|
868
|
+
await logger.aerror(f"Runtime error loading custom component {component_name}: {e!s}")
|
869
|
+
logger.debug("Full traceback for runtime error", exc_info=True)
|
870
|
+
return None
|
871
|
+
|
872
|
+
# If we get here, the component wasn't found in any of the paths
|
873
|
+
await logger.awarning(f"Component {component_name} not found in any of the provided paths")
|
874
|
+
return None
|
875
|
+
|
876
|
+
|
877
|
+
_COMPONENT_TYPE_NAMES = {"Component", "CustomComponent"}
|