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
@@ -0,0 +1,165 @@
|
|
1
|
+
"""Dependency analysis utilities for custom components."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
import ast
|
6
|
+
import importlib.metadata as md
|
7
|
+
import sys
|
8
|
+
from dataclasses import asdict, dataclass
|
9
|
+
from functools import lru_cache
|
10
|
+
|
11
|
+
try:
|
12
|
+
STDLIB_MODULES: set[str] = set(sys.stdlib_module_names) # 3.10+
|
13
|
+
except AttributeError:
|
14
|
+
# Fallback heuristic if running on <3.10
|
15
|
+
STDLIB_MODULES = set(sys.builtin_module_names)
|
16
|
+
|
17
|
+
|
18
|
+
@dataclass(frozen=True)
|
19
|
+
class DependencyInfo:
|
20
|
+
"""Information about a dependency imported in Python code."""
|
21
|
+
|
22
|
+
name: str # package name (e.g. "numpy", "requests")
|
23
|
+
version: str | None # package version if available
|
24
|
+
is_local: bool # True for relative imports (from .module import ...)
|
25
|
+
|
26
|
+
|
27
|
+
def _top_level(pkg: str) -> str:
|
28
|
+
"""Extract top-level package name."""
|
29
|
+
return pkg.split(".", 1)[0]
|
30
|
+
|
31
|
+
|
32
|
+
def _is_relative(module: str | None) -> bool:
|
33
|
+
"""Check if module is a relative import."""
|
34
|
+
return module is not None and module.startswith(".")
|
35
|
+
|
36
|
+
|
37
|
+
class _ImportVisitor(ast.NodeVisitor):
|
38
|
+
"""AST visitor to extract import information."""
|
39
|
+
|
40
|
+
def __init__(self):
|
41
|
+
self.results: list[DependencyInfo] = []
|
42
|
+
|
43
|
+
def visit_Import(self, node: ast.Import):
|
44
|
+
for alias in node.names:
|
45
|
+
full = alias.name
|
46
|
+
dep = DependencyInfo(
|
47
|
+
name=_top_level(full),
|
48
|
+
version=None,
|
49
|
+
is_local=False, # Regular imports are not local
|
50
|
+
)
|
51
|
+
self.results.append(dep)
|
52
|
+
|
53
|
+
def visit_ImportFrom(self, node: ast.ImportFrom):
|
54
|
+
# Reconstruct full module name with proper relative import handling
|
55
|
+
if node.level > 0:
|
56
|
+
# Relative import: from .module import x or from ..parent import x
|
57
|
+
dots = "." * node.level
|
58
|
+
full_module = dots + (node.module or "")
|
59
|
+
else:
|
60
|
+
# Absolute import: from module import x
|
61
|
+
full_module = node.module or ""
|
62
|
+
for _alias in node.names:
|
63
|
+
dep = DependencyInfo(
|
64
|
+
name=_top_level(full_module.lstrip(".")) if full_module else "",
|
65
|
+
version=None,
|
66
|
+
is_local=_is_relative(full_module), # Check if it's a relative import
|
67
|
+
)
|
68
|
+
self.results.append(dep)
|
69
|
+
|
70
|
+
|
71
|
+
def _classify_dependency(dep: DependencyInfo) -> DependencyInfo:
|
72
|
+
"""Resolve version information for external dependencies."""
|
73
|
+
version = None
|
74
|
+
if not dep.is_local and dep.name:
|
75
|
+
version = _get_distribution_version(dep.name)
|
76
|
+
|
77
|
+
return DependencyInfo(
|
78
|
+
name=dep.name,
|
79
|
+
version=version,
|
80
|
+
is_local=dep.is_local,
|
81
|
+
)
|
82
|
+
|
83
|
+
|
84
|
+
def analyze_dependencies(source: str, *, resolve_versions: bool = True) -> list[dict]:
|
85
|
+
"""Return a list[dict] of dependencies imported by the given Python source code.
|
86
|
+
|
87
|
+
Args:
|
88
|
+
source: Python source code string
|
89
|
+
resolve_versions: Whether to resolve version information
|
90
|
+
|
91
|
+
Returns:
|
92
|
+
List of dependency dictionaries
|
93
|
+
"""
|
94
|
+
code = source
|
95
|
+
|
96
|
+
# Parse the code and extract imports
|
97
|
+
tree = ast.parse(code)
|
98
|
+
visitor = _ImportVisitor()
|
99
|
+
visitor.visit(tree)
|
100
|
+
|
101
|
+
# Process and deduplicate dependencies by package name only
|
102
|
+
unique_packages: dict[str, DependencyInfo] = {}
|
103
|
+
for raw_dep in visitor.results:
|
104
|
+
processed_dep = _classify_dependency(raw_dep) if resolve_versions else raw_dep
|
105
|
+
|
106
|
+
# Skip stdlib imports and local imports - we only care about external dependencies
|
107
|
+
if processed_dep.name in STDLIB_MODULES or processed_dep.is_local:
|
108
|
+
continue
|
109
|
+
|
110
|
+
# Deduplicate by package name only (not full_module)
|
111
|
+
if processed_dep.name not in unique_packages:
|
112
|
+
unique_packages[processed_dep.name] = processed_dep
|
113
|
+
|
114
|
+
return [asdict(d) for d in unique_packages.values()]
|
115
|
+
|
116
|
+
|
117
|
+
def analyze_component_dependencies(component_code: str) -> dict:
|
118
|
+
"""Analyze dependencies for a custom component.
|
119
|
+
|
120
|
+
Args:
|
121
|
+
component_code: The component's source code
|
122
|
+
|
123
|
+
Returns:
|
124
|
+
Dictionary with dependency analysis results
|
125
|
+
"""
|
126
|
+
try:
|
127
|
+
deps = analyze_dependencies(component_code, resolve_versions=True)
|
128
|
+
|
129
|
+
return {
|
130
|
+
"total_dependencies": len(deps),
|
131
|
+
"dependencies": [{"name": d["name"], "version": d["version"]} for d in deps if d["name"]],
|
132
|
+
}
|
133
|
+
except (SyntaxError, TypeError, ValueError, ImportError):
|
134
|
+
# If analysis fails, return minimal info
|
135
|
+
return {
|
136
|
+
"total_dependencies": 0,
|
137
|
+
"dependencies": [],
|
138
|
+
}
|
139
|
+
|
140
|
+
|
141
|
+
# Cache the expensive packages_distributions() call globally
|
142
|
+
@lru_cache(maxsize=1)
|
143
|
+
def _get_packages_distributions():
|
144
|
+
"""Cache the expensive packages_distributions() call."""
|
145
|
+
try:
|
146
|
+
return md.packages_distributions()
|
147
|
+
except (OSError, AttributeError, ValueError):
|
148
|
+
return {}
|
149
|
+
|
150
|
+
|
151
|
+
# Helper function to cache version lookups for installed distributions
|
152
|
+
@lru_cache(maxsize=128)
|
153
|
+
def _get_distribution_version(import_name: str):
|
154
|
+
try:
|
155
|
+
# Reverse-lookup: which distribution(s) provide this importable name?
|
156
|
+
reverse_map = _get_packages_distributions()
|
157
|
+
dist_names = reverse_map.get(import_name)
|
158
|
+
if not dist_names:
|
159
|
+
return None
|
160
|
+
|
161
|
+
# Take the first matching distribution
|
162
|
+
dist_name = dist_names[0]
|
163
|
+
return md.distribution(dist_name).version
|
164
|
+
except (ImportError, AttributeError, OSError, ValueError):
|
165
|
+
return None
|
@@ -0,0 +1,359 @@
|
|
1
|
+
import ast
|
2
|
+
import asyncio
|
3
|
+
import zlib
|
4
|
+
from pathlib import Path
|
5
|
+
|
6
|
+
import anyio
|
7
|
+
from aiofile import async_open
|
8
|
+
|
9
|
+
from lfx.custom.custom_component.component import Component
|
10
|
+
from lfx.log.logger import logger
|
11
|
+
|
12
|
+
MAX_DEPTH = 2
|
13
|
+
|
14
|
+
|
15
|
+
class CustomComponentPathValueError(ValueError):
|
16
|
+
pass
|
17
|
+
|
18
|
+
|
19
|
+
class StringCompressor:
|
20
|
+
def __init__(self, input_string) -> None:
|
21
|
+
"""Initialize StringCompressor with a string to compress."""
|
22
|
+
self.input_string = input_string
|
23
|
+
|
24
|
+
def compress_string(self):
|
25
|
+
"""Compress the initial string and return the compressed data."""
|
26
|
+
# Convert string to bytes
|
27
|
+
byte_data = self.input_string.encode("utf-8")
|
28
|
+
# Compress the bytes
|
29
|
+
self.compressed_data = zlib.compress(byte_data)
|
30
|
+
|
31
|
+
return self.compressed_data
|
32
|
+
|
33
|
+
def decompress_string(self):
|
34
|
+
"""Decompress the compressed data and return the original string."""
|
35
|
+
# Decompress the bytes
|
36
|
+
decompressed_data = zlib.decompress(self.compressed_data)
|
37
|
+
# Convert bytes back to string
|
38
|
+
return decompressed_data.decode("utf-8")
|
39
|
+
|
40
|
+
|
41
|
+
class DirectoryReader:
|
42
|
+
# Ensure the base path to read the files that contain
|
43
|
+
# the custom components from this directory.
|
44
|
+
base_path = ""
|
45
|
+
|
46
|
+
def __init__(self, directory_path, *, compress_code_field=False) -> None:
|
47
|
+
"""Initialize DirectoryReader with a directory path and a flag indicating whether to compress the code."""
|
48
|
+
self.directory_path = directory_path
|
49
|
+
self.compress_code_field = compress_code_field
|
50
|
+
|
51
|
+
def get_safe_path(self):
|
52
|
+
"""Check if the path is valid and return it, or None if it's not."""
|
53
|
+
return self.directory_path if self.is_valid_path() else None
|
54
|
+
|
55
|
+
def is_valid_path(self) -> bool:
|
56
|
+
"""Check if the directory path is valid by comparing it to the base path."""
|
57
|
+
fullpath = Path(self.directory_path).resolve()
|
58
|
+
return not self.base_path or fullpath.is_relative_to(self.base_path)
|
59
|
+
|
60
|
+
def is_empty_file(self, file_content):
|
61
|
+
"""Check if the file content is empty."""
|
62
|
+
return len(file_content.strip()) == 0
|
63
|
+
|
64
|
+
def filter_loaded_components(self, data: dict, *, with_errors: bool) -> dict:
|
65
|
+
from lfx.custom.utils import build_component
|
66
|
+
|
67
|
+
items = []
|
68
|
+
for menu in data["menu"]:
|
69
|
+
components = []
|
70
|
+
for component in menu["components"]:
|
71
|
+
try:
|
72
|
+
if component["error"] if with_errors else not component["error"]:
|
73
|
+
component_tuple = (*build_component(component), component)
|
74
|
+
components.append(component_tuple)
|
75
|
+
except Exception: # noqa: BLE001
|
76
|
+
logger.debug(f"Error while loading component {component['name']} from {component['file']}")
|
77
|
+
continue
|
78
|
+
items.append({"name": menu["name"], "path": menu["path"], "components": components})
|
79
|
+
filtered = [menu for menu in items if menu["components"]]
|
80
|
+
logger.debug(f"Filtered components {'with errors' if with_errors else ''}: {len(filtered)}")
|
81
|
+
return {"menu": filtered}
|
82
|
+
|
83
|
+
def validate_code(self, file_content) -> bool:
|
84
|
+
"""Validate the Python code by trying to parse it with ast.parse."""
|
85
|
+
try:
|
86
|
+
ast.parse(file_content)
|
87
|
+
except SyntaxError:
|
88
|
+
return False
|
89
|
+
return True
|
90
|
+
|
91
|
+
def validate_build(self, file_content):
|
92
|
+
"""Check if the file content contains a function named 'build'."""
|
93
|
+
return "def build" in file_content
|
94
|
+
|
95
|
+
def read_file_content(self, file_path):
|
96
|
+
"""Read and return the content of a file."""
|
97
|
+
file_path_ = Path(file_path)
|
98
|
+
if not file_path_.is_file():
|
99
|
+
return None
|
100
|
+
try:
|
101
|
+
with file_path_.open(encoding="utf-8") as file:
|
102
|
+
# UnicodeDecodeError: 'charmap' codec can't decode byte 0x9d in position 3069:
|
103
|
+
# character maps to <undefined>
|
104
|
+
return file.read()
|
105
|
+
except UnicodeDecodeError:
|
106
|
+
# This is happening in Windows, so we need to open the file in binary mode
|
107
|
+
# The file is always just a python file, so we can safely read it as utf-8
|
108
|
+
with file_path_.open("rb") as f:
|
109
|
+
return f.read().decode("utf-8")
|
110
|
+
|
111
|
+
async def aread_file_content(self, file_path):
|
112
|
+
"""Read and return the content of a file."""
|
113
|
+
file_path_ = anyio.Path(file_path)
|
114
|
+
if not await file_path_.is_file():
|
115
|
+
return None
|
116
|
+
try:
|
117
|
+
async with async_open(str(file_path_), encoding="utf-8") as file:
|
118
|
+
# UnicodeDecodeError: 'charmap' codec can't decode byte 0x9d in position 3069:
|
119
|
+
# character maps to <undefined>
|
120
|
+
return await file.read()
|
121
|
+
except UnicodeDecodeError:
|
122
|
+
# This is happening in Windows, so we need to open the file in binary mode
|
123
|
+
# The file is always just a python file, so we can safely read it as utf-8
|
124
|
+
async with async_open(str(file_path_), "rb") as f:
|
125
|
+
return (await f.read()).decode("utf-8")
|
126
|
+
|
127
|
+
def get_files(self):
|
128
|
+
"""Walk through the directory path and return a list of all .py files."""
|
129
|
+
if not (safe_path := self.get_safe_path()):
|
130
|
+
msg = f"The path needs to start with '{self.base_path}'."
|
131
|
+
raise CustomComponentPathValueError(msg)
|
132
|
+
|
133
|
+
file_list = []
|
134
|
+
safe_path_obj = Path(safe_path)
|
135
|
+
for file_path in safe_path_obj.rglob("*.py"):
|
136
|
+
# Check if the file is in the folder `deactivated` and if so, skip it
|
137
|
+
if "deactivated" in file_path.parent.name:
|
138
|
+
continue
|
139
|
+
|
140
|
+
# Calculate the depth of the file relative to the safe path
|
141
|
+
relative_depth = len(file_path.relative_to(safe_path_obj).parts)
|
142
|
+
|
143
|
+
# Only include files that are one or two levels deep
|
144
|
+
if relative_depth <= MAX_DEPTH and file_path.is_file() and not file_path.name.startswith("__"):
|
145
|
+
file_list.append(str(file_path))
|
146
|
+
return file_list
|
147
|
+
|
148
|
+
def find_menu(self, response, menu_name):
|
149
|
+
"""Find and return a menu by its name in the response."""
|
150
|
+
return next(
|
151
|
+
(menu for menu in response["menu"] if menu["name"] == menu_name),
|
152
|
+
None,
|
153
|
+
)
|
154
|
+
|
155
|
+
def _is_type_hint_imported(self, type_hint_name: str, code: str) -> bool:
|
156
|
+
"""Check if a specific type hint is imported from the typing module in the given code."""
|
157
|
+
module = ast.parse(code)
|
158
|
+
|
159
|
+
return any(
|
160
|
+
isinstance(node, ast.ImportFrom)
|
161
|
+
and node.module == "typing"
|
162
|
+
and any(alias.name == type_hint_name for alias in node.names)
|
163
|
+
for node in ast.walk(module)
|
164
|
+
)
|
165
|
+
|
166
|
+
def _is_type_hint_used_in_args(self, type_hint_name: str, code: str) -> bool:
|
167
|
+
"""Check if a specific type hint is used in the function definitions within the given code."""
|
168
|
+
try:
|
169
|
+
module = ast.parse(code)
|
170
|
+
|
171
|
+
for node in ast.walk(module):
|
172
|
+
if isinstance(node, ast.FunctionDef):
|
173
|
+
for arg in node.args.args:
|
174
|
+
if self._is_type_hint_in_arg_annotation(arg.annotation, type_hint_name):
|
175
|
+
return True
|
176
|
+
except SyntaxError:
|
177
|
+
# Returns False if the code is not valid Python
|
178
|
+
return False
|
179
|
+
return False
|
180
|
+
|
181
|
+
def _is_type_hint_in_arg_annotation(self, annotation, type_hint_name: str) -> bool:
|
182
|
+
"""Helper function to check if a type hint exists in an annotation."""
|
183
|
+
return (
|
184
|
+
annotation is not None
|
185
|
+
and isinstance(annotation, ast.Subscript)
|
186
|
+
and isinstance(annotation.value, ast.Name)
|
187
|
+
and annotation.value.id == type_hint_name
|
188
|
+
)
|
189
|
+
|
190
|
+
def is_type_hint_used_but_not_imported(self, type_hint_name: str, code: str) -> bool:
|
191
|
+
"""Check if a type hint is used but not imported in the given code."""
|
192
|
+
try:
|
193
|
+
return self._is_type_hint_used_in_args(type_hint_name, code) and not self._is_type_hint_imported(
|
194
|
+
type_hint_name, code
|
195
|
+
)
|
196
|
+
except SyntaxError:
|
197
|
+
# Returns True if there's something wrong with the code
|
198
|
+
# TODO : Find a better way to handle this
|
199
|
+
return True
|
200
|
+
|
201
|
+
def process_file(self, file_path):
|
202
|
+
"""Process a file by validating its content and returning the result and content/error message."""
|
203
|
+
try:
|
204
|
+
file_content = self.read_file_content(file_path)
|
205
|
+
except Exception: # noqa: BLE001
|
206
|
+
logger.exception(f"Error while reading file {file_path}")
|
207
|
+
return False, f"Could not read {file_path}"
|
208
|
+
|
209
|
+
if file_content is None:
|
210
|
+
return False, f"Could not read {file_path}"
|
211
|
+
if self.is_empty_file(file_content):
|
212
|
+
return False, "Empty file"
|
213
|
+
if not self.validate_code(file_content):
|
214
|
+
return False, "Syntax error"
|
215
|
+
if self._is_type_hint_used_in_args("Optional", file_content) and not self._is_type_hint_imported(
|
216
|
+
"Optional", file_content
|
217
|
+
):
|
218
|
+
return (
|
219
|
+
False,
|
220
|
+
"Type hint 'Optional' is used but not imported in the code.",
|
221
|
+
)
|
222
|
+
if self.compress_code_field:
|
223
|
+
file_content = str(StringCompressor(file_content).compress_string())
|
224
|
+
return True, file_content
|
225
|
+
|
226
|
+
def build_component_menu_list(self, file_paths):
|
227
|
+
"""Build a list of menus with their components from the .py files in the directory."""
|
228
|
+
response = {"menu": []}
|
229
|
+
logger.debug("-------------------- Building component menu list --------------------")
|
230
|
+
|
231
|
+
for file_path in file_paths:
|
232
|
+
file_path_ = Path(file_path)
|
233
|
+
menu_name = file_path_.parent.name
|
234
|
+
filename = file_path_.name
|
235
|
+
validation_result, result_content = self.process_file(file_path)
|
236
|
+
if not validation_result:
|
237
|
+
logger.error(f"Error while processing file {file_path}")
|
238
|
+
|
239
|
+
menu_result = self.find_menu(response, menu_name) or {
|
240
|
+
"name": menu_name,
|
241
|
+
"path": str(file_path_.parent),
|
242
|
+
"components": [],
|
243
|
+
}
|
244
|
+
component_name = filename.split(".")[0]
|
245
|
+
# This is the name of the file which will be displayed in the UI
|
246
|
+
# We need to change it from snake_case to CamelCase
|
247
|
+
|
248
|
+
# first check if it's already CamelCase
|
249
|
+
if "_" in component_name:
|
250
|
+
component_name_camelcase = " ".join(word.title() for word in component_name.split("_"))
|
251
|
+
else:
|
252
|
+
component_name_camelcase = component_name
|
253
|
+
|
254
|
+
if validation_result:
|
255
|
+
try:
|
256
|
+
output_types = self.get_output_types_from_code(result_content)
|
257
|
+
except Exception: # noqa: BLE001
|
258
|
+
logger.debug("Error while getting output types from code", exc_info=True)
|
259
|
+
output_types = [component_name_camelcase]
|
260
|
+
else:
|
261
|
+
output_types = [component_name_camelcase]
|
262
|
+
|
263
|
+
component_info = {
|
264
|
+
"name": component_name_camelcase,
|
265
|
+
"output_types": output_types,
|
266
|
+
"file": filename,
|
267
|
+
"code": result_content if validation_result else "",
|
268
|
+
"error": "" if validation_result else result_content,
|
269
|
+
}
|
270
|
+
menu_result["components"].append(component_info)
|
271
|
+
|
272
|
+
if menu_result not in response["menu"]:
|
273
|
+
response["menu"].append(menu_result)
|
274
|
+
logger.debug("-------------------- Component menu list built --------------------")
|
275
|
+
return response
|
276
|
+
|
277
|
+
async def process_file_async(self, file_path):
|
278
|
+
try:
|
279
|
+
file_content = await self.aread_file_content(file_path)
|
280
|
+
except Exception: # noqa: BLE001
|
281
|
+
await logger.aexception(f"Error while reading file {file_path}")
|
282
|
+
return False, f"Could not read {file_path}"
|
283
|
+
|
284
|
+
if file_content is None:
|
285
|
+
return False, f"Could not read {file_path}"
|
286
|
+
if self.is_empty_file(file_content):
|
287
|
+
return False, "Empty file"
|
288
|
+
if not self.validate_code(file_content):
|
289
|
+
return False, "Syntax error"
|
290
|
+
if self._is_type_hint_used_in_args("Optional", file_content) and not self._is_type_hint_imported(
|
291
|
+
"Optional", file_content
|
292
|
+
):
|
293
|
+
return (
|
294
|
+
False,
|
295
|
+
"Type hint 'Optional' is used but not imported in the code.",
|
296
|
+
)
|
297
|
+
if self.compress_code_field:
|
298
|
+
file_content = str(StringCompressor(file_content).compress_string())
|
299
|
+
return True, file_content
|
300
|
+
|
301
|
+
async def abuild_component_menu_list(self, file_paths):
|
302
|
+
response = {"menu": []}
|
303
|
+
await logger.adebug("-------------------- Async Building component menu list --------------------")
|
304
|
+
|
305
|
+
tasks = [self.process_file_async(file_path) for file_path in file_paths]
|
306
|
+
results = await asyncio.gather(*tasks)
|
307
|
+
|
308
|
+
for file_path, (validation_result, result_content) in zip(file_paths, results, strict=True):
|
309
|
+
file_path_ = Path(file_path)
|
310
|
+
menu_name = file_path_.parent.name
|
311
|
+
filename = file_path_.name
|
312
|
+
|
313
|
+
if not validation_result:
|
314
|
+
await logger.aerror(f"Error while processing file {file_path}")
|
315
|
+
|
316
|
+
menu_result = self.find_menu(response, menu_name) or {
|
317
|
+
"name": menu_name,
|
318
|
+
"path": str(file_path_.parent),
|
319
|
+
"components": [],
|
320
|
+
}
|
321
|
+
component_name = filename.split(".")[0]
|
322
|
+
|
323
|
+
if "_" in component_name:
|
324
|
+
component_name_camelcase = " ".join(word.title() for word in component_name.split("_"))
|
325
|
+
else:
|
326
|
+
component_name_camelcase = component_name
|
327
|
+
|
328
|
+
if validation_result:
|
329
|
+
try:
|
330
|
+
output_types = await asyncio.to_thread(self.get_output_types_from_code, result_content)
|
331
|
+
except Exception: # noqa: BLE001
|
332
|
+
await logger.aexception("Error while getting output types from code")
|
333
|
+
output_types = [component_name_camelcase]
|
334
|
+
else:
|
335
|
+
output_types = [component_name_camelcase]
|
336
|
+
|
337
|
+
component_info = {
|
338
|
+
"name": component_name_camelcase,
|
339
|
+
"output_types": output_types,
|
340
|
+
"file": filename,
|
341
|
+
"code": result_content if validation_result else "",
|
342
|
+
"error": "" if validation_result else result_content,
|
343
|
+
}
|
344
|
+
menu_result["components"].append(component_info)
|
345
|
+
|
346
|
+
if menu_result not in response["menu"]:
|
347
|
+
response["menu"].append(menu_result)
|
348
|
+
|
349
|
+
await logger.adebug("-------------------- Component menu list built --------------------")
|
350
|
+
return response
|
351
|
+
|
352
|
+
@staticmethod
|
353
|
+
def get_output_types_from_code(code: str) -> list:
|
354
|
+
"""Get the output types from the code."""
|
355
|
+
custom_component = Component(_code=code)
|
356
|
+
types_list = custom_component._get_function_entrypoint_return_type
|
357
|
+
|
358
|
+
# Get the name of types classes
|
359
|
+
return [type_.__name__ for type_ in types_list if hasattr(type_, "__name__")]
|