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/cli/common.py
ADDED
@@ -0,0 +1,650 @@
|
|
1
|
+
"""Common utilities for CLI commands."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
import ast
|
6
|
+
import contextlib
|
7
|
+
import importlib.metadata as importlib_metadata
|
8
|
+
import io
|
9
|
+
import os
|
10
|
+
import re
|
11
|
+
import socket
|
12
|
+
import subprocess
|
13
|
+
import sys
|
14
|
+
import tempfile
|
15
|
+
import uuid
|
16
|
+
import zipfile
|
17
|
+
from io import StringIO
|
18
|
+
from pathlib import Path
|
19
|
+
from shutil import which
|
20
|
+
from typing import TYPE_CHECKING
|
21
|
+
from urllib.parse import urlparse
|
22
|
+
|
23
|
+
import httpx
|
24
|
+
import typer
|
25
|
+
|
26
|
+
from lfx.cli.script_loader import (
|
27
|
+
extract_structured_result,
|
28
|
+
find_graph_variable,
|
29
|
+
load_graph_from_script,
|
30
|
+
)
|
31
|
+
from lfx.load import load_flow_from_json
|
32
|
+
from lfx.schema.schema import InputValueRequest
|
33
|
+
|
34
|
+
if TYPE_CHECKING:
|
35
|
+
from types import ModuleType
|
36
|
+
|
37
|
+
# Attempt to import tomllib (3.11+) else fall back to tomli
|
38
|
+
_toml_parser: ModuleType | None = None
|
39
|
+
try:
|
40
|
+
import tomllib as _toml_parser
|
41
|
+
except ModuleNotFoundError:
|
42
|
+
with contextlib.suppress(ModuleNotFoundError):
|
43
|
+
import tomli as toml_parser
|
44
|
+
|
45
|
+
_toml_parser = toml_parser
|
46
|
+
|
47
|
+
MAX_PORT_NUMBER = 65535
|
48
|
+
|
49
|
+
# Fixed namespace constant for deterministic UUID5 generation across runs
|
50
|
+
_LANGFLOW_NAMESPACE_UUID = uuid.UUID("3c091057-e799-4e32-8ebc-27bc31e1108c")
|
51
|
+
|
52
|
+
# Environment variable for GitHub token
|
53
|
+
_GITHUB_TOKEN_ENV = "GITHUB_TOKEN"
|
54
|
+
|
55
|
+
|
56
|
+
def create_verbose_printer(*, verbose: bool):
|
57
|
+
"""Create a verbose printer function that only prints in verbose mode.
|
58
|
+
|
59
|
+
Args:
|
60
|
+
verbose: Whether to print verbose output
|
61
|
+
|
62
|
+
Returns:
|
63
|
+
Function that prints to stderr only in verbose mode
|
64
|
+
"""
|
65
|
+
|
66
|
+
def verbose_print(message: str) -> None:
|
67
|
+
"""Print diagnostic messages to stderr only in verbose mode."""
|
68
|
+
if verbose:
|
69
|
+
typer.echo(message, file=sys.stderr)
|
70
|
+
|
71
|
+
return verbose_print
|
72
|
+
|
73
|
+
|
74
|
+
def is_port_in_use(port: int, host: str = "localhost") -> bool:
|
75
|
+
"""Check if a port is already in use."""
|
76
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
77
|
+
try:
|
78
|
+
s.bind((host, port))
|
79
|
+
except OSError:
|
80
|
+
return True
|
81
|
+
else:
|
82
|
+
return False
|
83
|
+
|
84
|
+
|
85
|
+
def get_free_port(starting_port: int = 8000) -> int:
|
86
|
+
"""Get a free port starting from the given port."""
|
87
|
+
port = starting_port
|
88
|
+
while port < MAX_PORT_NUMBER:
|
89
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
90
|
+
try:
|
91
|
+
s.bind(("", port))
|
92
|
+
except OSError:
|
93
|
+
port += 1
|
94
|
+
else:
|
95
|
+
return port
|
96
|
+
msg = "No free ports available"
|
97
|
+
raise RuntimeError(msg)
|
98
|
+
|
99
|
+
|
100
|
+
def get_best_access_host(host: str) -> str:
|
101
|
+
"""Get the best host address for external access."""
|
102
|
+
# Note: 0.0.0.0 and :: are intentionally checked as they bind to all interfaces
|
103
|
+
if host in ("0.0.0.0", "::", ""):
|
104
|
+
return "localhost"
|
105
|
+
return host
|
106
|
+
|
107
|
+
|
108
|
+
def get_api_key() -> str:
|
109
|
+
"""Get the API key from environment variable."""
|
110
|
+
api_key = os.getenv("LANGFLOW_API_KEY")
|
111
|
+
if not api_key:
|
112
|
+
msg = "LANGFLOW_API_KEY environment variable is not set"
|
113
|
+
raise ValueError(msg)
|
114
|
+
return api_key
|
115
|
+
|
116
|
+
|
117
|
+
def is_url(path_or_url: str) -> bool:
|
118
|
+
"""Check if the given string is a URL.
|
119
|
+
|
120
|
+
Args:
|
121
|
+
path_or_url: String to check
|
122
|
+
|
123
|
+
Returns:
|
124
|
+
True if it's a URL, False otherwise
|
125
|
+
"""
|
126
|
+
try:
|
127
|
+
result = urlparse(path_or_url)
|
128
|
+
return all([result.scheme, result.netloc])
|
129
|
+
except Exception: # noqa: BLE001
|
130
|
+
return False
|
131
|
+
|
132
|
+
|
133
|
+
def download_script_from_url(url: str, verbose_print) -> Path:
|
134
|
+
"""Download a Python script from a URL and save it to a temporary file.
|
135
|
+
|
136
|
+
Args:
|
137
|
+
url: URL to download the script from
|
138
|
+
verbose_print: Function to print verbose messages
|
139
|
+
|
140
|
+
Returns:
|
141
|
+
Path to the temporary file containing the downloaded script
|
142
|
+
|
143
|
+
Raises:
|
144
|
+
typer.Exit: If download fails
|
145
|
+
"""
|
146
|
+
verbose_print(f"Downloading script from URL: {url}")
|
147
|
+
|
148
|
+
try:
|
149
|
+
with httpx.Client(timeout=30.0) as client:
|
150
|
+
response = client.get(url)
|
151
|
+
response.raise_for_status()
|
152
|
+
|
153
|
+
# Check if the response is a Python script
|
154
|
+
content_type = response.headers.get("content-type", "").lower()
|
155
|
+
valid_types = {"application/x-python", "application/octet-stream"}
|
156
|
+
if not (content_type.startswith("text/") or content_type in valid_types):
|
157
|
+
verbose_print(f"Warning: Unexpected content type: {content_type}")
|
158
|
+
|
159
|
+
# Create a temporary file with .py extension
|
160
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as temp_file:
|
161
|
+
temp_path = Path(temp_file.name)
|
162
|
+
|
163
|
+
# Write the content to the temporary file
|
164
|
+
script_content = response.text
|
165
|
+
temp_file.write(script_content)
|
166
|
+
|
167
|
+
verbose_print(f"✓ Script downloaded successfully to temporary file: {temp_path}")
|
168
|
+
return temp_path
|
169
|
+
|
170
|
+
except httpx.HTTPStatusError as e:
|
171
|
+
msg = f"✗ HTTP error downloading script: {e.response.status_code} - {e.response.text}"
|
172
|
+
verbose_print(msg)
|
173
|
+
raise typer.Exit(1) from e
|
174
|
+
except httpx.RequestError as e:
|
175
|
+
msg = f"✗ Network error downloading script: {e}"
|
176
|
+
verbose_print(msg)
|
177
|
+
raise typer.Exit(1) from e
|
178
|
+
except Exception as e:
|
179
|
+
msg = f"✗ Unexpected error downloading script: {e}"
|
180
|
+
verbose_print(msg)
|
181
|
+
raise typer.Exit(1) from e
|
182
|
+
|
183
|
+
|
184
|
+
def validate_script_path(script_path: Path | str, verbose_print) -> tuple[str, Path]:
|
185
|
+
"""Validate script path or URL and return file extension and resolved path.
|
186
|
+
|
187
|
+
Args:
|
188
|
+
script_path: Path to the script file or URL
|
189
|
+
verbose_print: Function to print verbose messages
|
190
|
+
|
191
|
+
Returns:
|
192
|
+
Tuple of (file_extension, resolved_path)
|
193
|
+
|
194
|
+
Raises:
|
195
|
+
typer.Exit: If validation fails
|
196
|
+
"""
|
197
|
+
# Handle URL case
|
198
|
+
if isinstance(script_path, str) and is_url(script_path):
|
199
|
+
resolved_path = download_script_from_url(script_path, verbose_print)
|
200
|
+
file_extension = resolved_path.suffix.lower()
|
201
|
+
if file_extension != ".py":
|
202
|
+
verbose_print(f"Error: URL must point to a Python script (.py file), got: {file_extension}")
|
203
|
+
raise typer.Exit(1)
|
204
|
+
return file_extension, resolved_path
|
205
|
+
|
206
|
+
# Handle local file case
|
207
|
+
if isinstance(script_path, str):
|
208
|
+
script_path = Path(script_path)
|
209
|
+
|
210
|
+
if not script_path.exists():
|
211
|
+
verbose_print(f"Error: File '{script_path}' does not exist.")
|
212
|
+
raise typer.Exit(1)
|
213
|
+
|
214
|
+
if not script_path.is_file():
|
215
|
+
verbose_print(f"Error: '{script_path}' is not a file.")
|
216
|
+
raise typer.Exit(1)
|
217
|
+
|
218
|
+
# Check file extension and validate
|
219
|
+
file_extension = script_path.suffix.lower()
|
220
|
+
if file_extension not in [".py", ".json"]:
|
221
|
+
verbose_print(f"Error: '{script_path}' must be a .py or .json file.")
|
222
|
+
raise typer.Exit(1)
|
223
|
+
|
224
|
+
return file_extension, script_path
|
225
|
+
|
226
|
+
|
227
|
+
def load_graph_from_path(script_path: Path, file_extension: str, verbose_print, *, verbose: bool = False):
|
228
|
+
"""Load a graph from a Python script or JSON file.
|
229
|
+
|
230
|
+
Args:
|
231
|
+
script_path: Path to the script file
|
232
|
+
file_extension: File extension (.py or .json)
|
233
|
+
verbose_print: Function to print verbose messages
|
234
|
+
verbose: Whether verbose mode is enabled
|
235
|
+
|
236
|
+
Returns:
|
237
|
+
Loaded graph object
|
238
|
+
|
239
|
+
Raises:
|
240
|
+
typer.Exit: If loading fails
|
241
|
+
"""
|
242
|
+
file_type = "Python script" if file_extension == ".py" else "JSON flow"
|
243
|
+
verbose_print(f"Analyzing {file_type}: {script_path}")
|
244
|
+
|
245
|
+
try:
|
246
|
+
if file_extension == ".py":
|
247
|
+
verbose_print("Analyzing Python script...")
|
248
|
+
graph_var = find_graph_variable(script_path)
|
249
|
+
if graph_var:
|
250
|
+
source_info = graph_var.get("source", "Unknown")
|
251
|
+
type_info = graph_var.get("type", "Unknown")
|
252
|
+
line_no = graph_var.get("line", "Unknown")
|
253
|
+
verbose_print(f"✓ Found 'graph' variable at line {line_no}")
|
254
|
+
verbose_print(f" Type: {type_info}")
|
255
|
+
verbose_print(f" Source: {source_info}")
|
256
|
+
else:
|
257
|
+
error_msg = "No 'graph' variable found in script"
|
258
|
+
verbose_print(f"✗ {error_msg}")
|
259
|
+
raise ValueError(error_msg)
|
260
|
+
|
261
|
+
verbose_print("Loading graph...")
|
262
|
+
graph = load_graph_from_script(script_path)
|
263
|
+
else: # .json
|
264
|
+
verbose_print("Loading JSON flow...")
|
265
|
+
graph = load_flow_from_json(script_path, disable_logs=not verbose)
|
266
|
+
|
267
|
+
except ValueError as e:
|
268
|
+
# Re-raise ValueError as typer.Exit to preserve the error message
|
269
|
+
raise typer.Exit(1) from e
|
270
|
+
except Exception as e:
|
271
|
+
verbose_print(f"✗ Failed to load graph: {e}")
|
272
|
+
raise typer.Exit(1) from e
|
273
|
+
else:
|
274
|
+
return graph
|
275
|
+
|
276
|
+
|
277
|
+
def prepare_graph(graph, verbose_print):
|
278
|
+
"""Prepare a graph for execution.
|
279
|
+
|
280
|
+
Args:
|
281
|
+
graph: Graph object to prepare
|
282
|
+
verbose_print: Function to print verbose messages
|
283
|
+
|
284
|
+
Raises:
|
285
|
+
typer.Exit: If preparation fails
|
286
|
+
"""
|
287
|
+
verbose_print("Preparing graph for execution...")
|
288
|
+
try:
|
289
|
+
graph.prepare()
|
290
|
+
verbose_print("✓ Graph prepared successfully")
|
291
|
+
except Exception as e:
|
292
|
+
verbose_print(f"✗ Failed to prepare graph: {e}")
|
293
|
+
raise typer.Exit(1) from e
|
294
|
+
|
295
|
+
|
296
|
+
async def execute_graph_with_capture(graph, input_value: str | None):
|
297
|
+
"""Execute a graph and capture output.
|
298
|
+
|
299
|
+
Args:
|
300
|
+
graph: Graph object to execute
|
301
|
+
input_value: Input value to pass to the graph
|
302
|
+
|
303
|
+
Returns:
|
304
|
+
Tuple of (results, captured_logs)
|
305
|
+
|
306
|
+
Raises:
|
307
|
+
Exception: Re-raises any exception that occurs during graph execution
|
308
|
+
"""
|
309
|
+
# Create input request
|
310
|
+
inputs = InputValueRequest(input_value=input_value) if input_value else None
|
311
|
+
|
312
|
+
# Capture output during execution
|
313
|
+
captured_stdout = StringIO()
|
314
|
+
captured_stderr = StringIO()
|
315
|
+
|
316
|
+
# Redirect stdout and stderr during graph execution
|
317
|
+
original_stdout = sys.stdout
|
318
|
+
original_stderr = sys.stderr
|
319
|
+
|
320
|
+
try:
|
321
|
+
sys.stdout = captured_stdout
|
322
|
+
sys.stderr = captured_stderr
|
323
|
+
results = [result async for result in graph.async_start(inputs)]
|
324
|
+
except Exception as exc:
|
325
|
+
# Capture any error output that was written to stderr
|
326
|
+
error_output = captured_stderr.getvalue()
|
327
|
+
if error_output:
|
328
|
+
# Add error output to the exception for better debugging
|
329
|
+
exc.args = (f"{exc.args[0] if exc.args else str(exc)}\n\nCaptured stderr:\n{error_output}",)
|
330
|
+
raise
|
331
|
+
finally:
|
332
|
+
# Restore original stdout/stderr
|
333
|
+
sys.stdout = original_stdout
|
334
|
+
sys.stderr = original_stderr
|
335
|
+
|
336
|
+
# Get captured logs
|
337
|
+
captured_logs = captured_stdout.getvalue() + captured_stderr.getvalue()
|
338
|
+
|
339
|
+
return results, captured_logs
|
340
|
+
|
341
|
+
|
342
|
+
def extract_result_data(results, captured_logs: str) -> dict:
|
343
|
+
"""Extract structured result data from graph execution results.
|
344
|
+
|
345
|
+
Args:
|
346
|
+
results: Graph execution results
|
347
|
+
captured_logs: Captured output logs
|
348
|
+
|
349
|
+
Returns:
|
350
|
+
Structured result data dictionary
|
351
|
+
"""
|
352
|
+
result_data = extract_structured_result(results)
|
353
|
+
result_data["logs"] = captured_logs
|
354
|
+
return result_data
|
355
|
+
|
356
|
+
|
357
|
+
# --- Dependency helpers ------------------------------------------------------------------
|
358
|
+
|
359
|
+
|
360
|
+
def _parse_pep723_block(script_path: Path, verbose_print) -> dict | None:
|
361
|
+
"""Extract the TOML table contained in a PEP-723 inline metadata block.
|
362
|
+
|
363
|
+
Args:
|
364
|
+
script_path: Path to the Python script to inspect.
|
365
|
+
verbose_print: Diagnostic printer.
|
366
|
+
|
367
|
+
Returns:
|
368
|
+
Parsed TOML dict if a block is found and successfully parsed, otherwise None.
|
369
|
+
"""
|
370
|
+
if _toml_parser is None:
|
371
|
+
verbose_print("tomllib/tomli not available - cannot parse inline dependencies")
|
372
|
+
return None
|
373
|
+
|
374
|
+
try:
|
375
|
+
lines = script_path.read_text(encoding="utf-8").splitlines()
|
376
|
+
except OSError as exc: # pragma: no cover
|
377
|
+
verbose_print(f"Failed reading script for dependency parsing: {exc}")
|
378
|
+
return None
|
379
|
+
|
380
|
+
# Locate `# /// script` and closing `# ///` markers.
|
381
|
+
try:
|
382
|
+
start_idx = next(i for i, ln in enumerate(lines) if ln.lstrip().startswith("# /// script")) + 1
|
383
|
+
end_idx = next(i for i, ln in enumerate(lines[start_idx:], start=start_idx) if ln.lstrip().startswith("# ///"))
|
384
|
+
except StopIteration:
|
385
|
+
return None # No valid block
|
386
|
+
|
387
|
+
# Remove leading comment markers and excess whitespace
|
388
|
+
block_lines: list[str] = []
|
389
|
+
for raw_line in lines[start_idx:end_idx]:
|
390
|
+
stripped_line = raw_line.lstrip()
|
391
|
+
if not stripped_line.startswith("#"):
|
392
|
+
continue
|
393
|
+
block_lines.append(stripped_line.lstrip("# "))
|
394
|
+
|
395
|
+
block_toml = "\n".join(block_lines).strip()
|
396
|
+
if not block_toml:
|
397
|
+
return None
|
398
|
+
|
399
|
+
try:
|
400
|
+
return _toml_parser.loads(block_toml)
|
401
|
+
except Exception as exc: # pragma: no cover # noqa: BLE001
|
402
|
+
verbose_print(f"Failed parsing TOML from PEP-723 block: {exc}")
|
403
|
+
return None
|
404
|
+
|
405
|
+
|
406
|
+
def extract_script_dependencies(script_path: Path, verbose_print) -> list[str]:
|
407
|
+
"""Return dependency strings declared via PEP-723 inline metadata.
|
408
|
+
|
409
|
+
Only `.py` files are supported for now. Returns an empty list if the file has
|
410
|
+
no metadata block or could not be parsed.
|
411
|
+
"""
|
412
|
+
if script_path.suffix != ".py":
|
413
|
+
return []
|
414
|
+
|
415
|
+
parsed = _parse_pep723_block(script_path, verbose_print)
|
416
|
+
if not parsed:
|
417
|
+
return []
|
418
|
+
|
419
|
+
deps = parsed.get("dependencies", [])
|
420
|
+
# Ensure list[str]
|
421
|
+
if isinstance(deps, list):
|
422
|
+
return [str(d).strip() for d in deps if str(d).strip()]
|
423
|
+
return []
|
424
|
+
|
425
|
+
|
426
|
+
def _needs_install(requirement: str) -> bool:
|
427
|
+
"""Heuristic: check if *some* distribution that satisfies the requirement is present.
|
428
|
+
|
429
|
+
Exact version resolution is delegated to the installer; here we do a best-effort
|
430
|
+
importlib.metadata lookup for the top-level name before the first comparison op.
|
431
|
+
"""
|
432
|
+
from packaging.requirements import Requirement # locally imported to avoid hard dep if unused
|
433
|
+
|
434
|
+
try:
|
435
|
+
req = Requirement(requirement)
|
436
|
+
except Exception: # noqa: BLE001
|
437
|
+
return True # If we cannot parse it, assume missing so installer handles it
|
438
|
+
|
439
|
+
try:
|
440
|
+
dist_version = importlib_metadata.version(req.name)
|
441
|
+
except importlib_metadata.PackageNotFoundError:
|
442
|
+
return True
|
443
|
+
|
444
|
+
# If specifier is empty, we already have it.
|
445
|
+
if not req.specifier:
|
446
|
+
return False
|
447
|
+
|
448
|
+
try:
|
449
|
+
from packaging.version import InvalidVersion, Version
|
450
|
+
except ImportError:
|
451
|
+
# If packaging is missing, we cannot compare - treat as missing.
|
452
|
+
return True
|
453
|
+
|
454
|
+
try:
|
455
|
+
if req.specifier.contains(Version(dist_version), prereleases=True):
|
456
|
+
return False
|
457
|
+
except InvalidVersion:
|
458
|
+
return True
|
459
|
+
|
460
|
+
return True
|
461
|
+
|
462
|
+
|
463
|
+
def ensure_dependencies_installed(dependencies: list[str], verbose_print) -> None:
|
464
|
+
"""Install missing dependencies using uv (preferred) or pip.
|
465
|
+
|
466
|
+
Args:
|
467
|
+
dependencies: List of requirement strings (PEP 508 style).
|
468
|
+
verbose_print: Diagnostic printer.
|
469
|
+
"""
|
470
|
+
if not dependencies:
|
471
|
+
return
|
472
|
+
|
473
|
+
missing = [req for req in dependencies if _needs_install(req)]
|
474
|
+
if not missing:
|
475
|
+
verbose_print("All script dependencies already satisfied")
|
476
|
+
return
|
477
|
+
|
478
|
+
installer_cmd: list[str]
|
479
|
+
if which("uv"):
|
480
|
+
installer_cmd = ["uv", "pip", "install", "--quiet", *missing]
|
481
|
+
tool_name = "uv"
|
482
|
+
else:
|
483
|
+
# Fall back to current interpreter's pip
|
484
|
+
installer_cmd = [sys.executable, "-m", "pip", "install", "--quiet", *missing]
|
485
|
+
tool_name = "pip"
|
486
|
+
|
487
|
+
verbose_print(f"Installing missing dependencies with {tool_name}: {', '.join(missing)}")
|
488
|
+
try:
|
489
|
+
subprocess.run(installer_cmd, check=True) # noqa: S603
|
490
|
+
verbose_print("✓ Dependency installation succeeded")
|
491
|
+
except subprocess.CalledProcessError as exc: # pragma: no cover
|
492
|
+
verbose_print(f"✗ Failed installing dependencies: {exc}")
|
493
|
+
raise typer.Exit(1) from exc
|
494
|
+
|
495
|
+
|
496
|
+
def flow_id_from_path(file_path: Path, root_dir: Path) -> str:
|
497
|
+
"""Generate a deterministic UUID-5 based flow id from *file_path*.
|
498
|
+
|
499
|
+
The function uses a fixed namespace UUID and the POSIX-style relative path
|
500
|
+
(relative to *root_dir*) as the *name* when calling :pyfunc:`uuid.uuid5`.
|
501
|
+
This guarantees:
|
502
|
+
|
503
|
+
1. The same folder deployed again produces identical flow IDs.
|
504
|
+
2. IDs remain stable even if the absolute location of the folder changes
|
505
|
+
(only the relative path is hashed).
|
506
|
+
3. Practically collision-free identifiers without maintaining external
|
507
|
+
state.
|
508
|
+
|
509
|
+
Args:
|
510
|
+
file_path: Path of the JSON flow file.
|
511
|
+
root_dir: Root directory from which *file_path* should be considered
|
512
|
+
relative. Typically the folder passed to the deploy command.
|
513
|
+
|
514
|
+
Returns:
|
515
|
+
-------
|
516
|
+
str
|
517
|
+
Canonical UUID string (36 chars, including hyphens).
|
518
|
+
"""
|
519
|
+
relative = file_path.relative_to(root_dir).as_posix()
|
520
|
+
return str(uuid.uuid5(_LANGFLOW_NAMESPACE_UUID, relative))
|
521
|
+
|
522
|
+
|
523
|
+
# ---------------------------------------------------------------------------
|
524
|
+
# GitHub / ZIP repository helpers (synchronous equivalents of initial_setup)
|
525
|
+
# ---------------------------------------------------------------------------
|
526
|
+
|
527
|
+
_GITHUB_RE_REPO = re.compile(r"https?://(?:www\.)?github\.com/([\w.-]+)/([\w.-]+)(?:\.git)?/?$")
|
528
|
+
_GITHUB_RE_TREE = re.compile(r"https?://(?:www\.)?github\.com/([\w.-]+)/([\w.-]+)/tree/([\w\/-]+)")
|
529
|
+
_GITHUB_RE_RELEASE = re.compile(r"https?://(?:www\.)?github\.com/([\w.-]+)/([\w.-]+)/releases/tag/([\w\/-]+)")
|
530
|
+
_GITHUB_RE_COMMIT = re.compile(r"https?://(?:www\.)?github\.com/([\w.-]+)/([\w.-]+)/commit/(\w+)(?:/)?$")
|
531
|
+
|
532
|
+
|
533
|
+
def _github_headers() -> dict[str, str]:
|
534
|
+
token = os.getenv(_GITHUB_TOKEN_ENV)
|
535
|
+
if token:
|
536
|
+
return {"Authorization": f"token {token}"}
|
537
|
+
return {}
|
538
|
+
|
539
|
+
|
540
|
+
def detect_github_url_sync(url: str, *, timeout: float = 15.0) -> str:
|
541
|
+
"""Convert various GitHub URLs into a direct `.zip` download link (sync).
|
542
|
+
|
543
|
+
Mirrors the async implementation in *initial_setup.setup.detect_github_url*.
|
544
|
+
"""
|
545
|
+
if match := _GITHUB_RE_REPO.match(url):
|
546
|
+
owner, repo = match.groups()
|
547
|
+
# Determine default branch via GitHub API
|
548
|
+
with httpx.Client(timeout=timeout, follow_redirects=True, headers=_github_headers()) as client:
|
549
|
+
resp = client.get(f"https://api.github.com/repos/{owner}/{repo}")
|
550
|
+
resp.raise_for_status()
|
551
|
+
default_branch = resp.json().get("default_branch", "main")
|
552
|
+
return f"https://github.com/{owner}/{repo}/archive/refs/heads/{default_branch}.zip"
|
553
|
+
|
554
|
+
if match := _GITHUB_RE_TREE.match(url):
|
555
|
+
owner, repo, branch = match.groups()
|
556
|
+
branch = branch.rstrip("/")
|
557
|
+
return f"https://github.com/{owner}/{repo}/archive/refs/heads/{branch}.zip"
|
558
|
+
|
559
|
+
if match := _GITHUB_RE_RELEASE.match(url):
|
560
|
+
owner, repo, tag = match.groups()
|
561
|
+
tag = tag.rstrip("/")
|
562
|
+
return f"https://github.com/{owner}/{repo}/archive/refs/tags/{tag}.zip"
|
563
|
+
|
564
|
+
if match := _GITHUB_RE_COMMIT.match(url):
|
565
|
+
owner, repo, commit = match.groups()
|
566
|
+
return f"https://github.com/{owner}/{repo}/archive/{commit}.zip"
|
567
|
+
|
568
|
+
# Not a recognized GitHub URL; assume it's already a direct link
|
569
|
+
return url
|
570
|
+
|
571
|
+
|
572
|
+
def download_and_extract_repo(url: str, verbose_print, *, timeout: float = 60.0) -> Path:
|
573
|
+
"""Download a ZIP archive from *url* and extract into a temp directory.
|
574
|
+
|
575
|
+
Returns the **root directory** containing the extracted files.
|
576
|
+
"""
|
577
|
+
verbose_print(f"Downloading repository/ZIP from {url}")
|
578
|
+
|
579
|
+
zip_url = detect_github_url_sync(url)
|
580
|
+
|
581
|
+
try:
|
582
|
+
with httpx.Client(timeout=timeout, follow_redirects=True, headers=_github_headers()) as client:
|
583
|
+
resp = client.get(zip_url)
|
584
|
+
resp.raise_for_status()
|
585
|
+
|
586
|
+
tmp_dir = tempfile.TemporaryDirectory()
|
587
|
+
with zipfile.ZipFile(io.BytesIO(resp.content)) as zf:
|
588
|
+
zf.extractall(tmp_dir.name)
|
589
|
+
|
590
|
+
verbose_print(f"✓ Repository extracted to {tmp_dir.name}")
|
591
|
+
|
592
|
+
# Most GitHub archives have a single top-level folder; use it if present
|
593
|
+
root_path = Path(tmp_dir.name)
|
594
|
+
sub_entries = list(root_path.iterdir())
|
595
|
+
if len(sub_entries) == 1 and sub_entries[0].is_dir():
|
596
|
+
root_path = sub_entries[0]
|
597
|
+
|
598
|
+
# Ensure root on sys.path for custom components
|
599
|
+
if str(root_path) not in sys.path:
|
600
|
+
sys.path.insert(0, str(root_path))
|
601
|
+
|
602
|
+
# Attach TemporaryDirectory to path object so caller can keep reference
|
603
|
+
# and prevent premature cleanup. We set attribute _tmp_dir.
|
604
|
+
root_path._tmp_dir = tmp_dir # type: ignore[attr-defined] # noqa: SLF001
|
605
|
+
|
606
|
+
except httpx.HTTPStatusError as e:
|
607
|
+
verbose_print(f"✗ HTTP error downloading ZIP: {e.response.status_code}")
|
608
|
+
raise
|
609
|
+
except Exception as exc:
|
610
|
+
verbose_print(f"✗ Failed downloading or extracting repo: {exc}")
|
611
|
+
raise
|
612
|
+
else:
|
613
|
+
return root_path
|
614
|
+
|
615
|
+
|
616
|
+
def extract_script_docstring(script_path: Path) -> str | None:
|
617
|
+
"""Extract the module-level docstring from a Python script.
|
618
|
+
|
619
|
+
Args:
|
620
|
+
script_path: Path to the Python script file
|
621
|
+
|
622
|
+
Returns:
|
623
|
+
The docstring text if found, None otherwise
|
624
|
+
"""
|
625
|
+
try:
|
626
|
+
# Read the file content
|
627
|
+
with script_path.open(encoding="utf-8") as f:
|
628
|
+
content = f.read()
|
629
|
+
|
630
|
+
# Parse the AST
|
631
|
+
tree = ast.parse(content)
|
632
|
+
|
633
|
+
# Check if the first statement is a docstring
|
634
|
+
# A docstring is a string literal that appears as the first statement
|
635
|
+
if (
|
636
|
+
tree.body
|
637
|
+
and isinstance(tree.body[0], ast.Expr)
|
638
|
+
and isinstance(tree.body[0].value, ast.Constant)
|
639
|
+
and isinstance(tree.body[0].value.value, str)
|
640
|
+
):
|
641
|
+
docstring = tree.body[0].value.value
|
642
|
+
# Clean up the docstring by removing extra whitespace
|
643
|
+
return docstring.strip()
|
644
|
+
|
645
|
+
except (OSError, SyntaxError, UnicodeDecodeError):
|
646
|
+
# If we can't read or parse the file, just return None
|
647
|
+
# Don't raise an error as this is optional functionality
|
648
|
+
pass
|
649
|
+
|
650
|
+
return None
|