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,311 @@
|
|
1
|
+
import time
|
2
|
+
from concurrent.futures import ThreadPoolExecutor
|
3
|
+
from pathlib import Path
|
4
|
+
from typing import Any
|
5
|
+
|
6
|
+
from tenacity import retry, stop_after_attempt, wait_exponential
|
7
|
+
from twelvelabs import TwelveLabs
|
8
|
+
|
9
|
+
from lfx.custom import Component
|
10
|
+
from lfx.inputs import DataInput, DropdownInput, SecretStrInput, StrInput
|
11
|
+
from lfx.io import Output
|
12
|
+
from lfx.schema import Data
|
13
|
+
|
14
|
+
|
15
|
+
class TwelveLabsError(Exception):
|
16
|
+
"""Base exception for TwelveLabs errors."""
|
17
|
+
|
18
|
+
|
19
|
+
class IndexCreationError(TwelveLabsError):
|
20
|
+
"""Error raised when there's an issue with an index."""
|
21
|
+
|
22
|
+
|
23
|
+
class TaskError(TwelveLabsError):
|
24
|
+
"""Error raised when a task fails."""
|
25
|
+
|
26
|
+
|
27
|
+
class TaskTimeoutError(TwelveLabsError):
|
28
|
+
"""Error raised when a task times out."""
|
29
|
+
|
30
|
+
|
31
|
+
class PegasusIndexVideo(Component):
|
32
|
+
"""Indexes videos using TwelveLabs Pegasus API and adds the video ID to metadata."""
|
33
|
+
|
34
|
+
display_name = "TwelveLabs Pegasus Index Video"
|
35
|
+
description = "Index videos using TwelveLabs and add the video_id to metadata."
|
36
|
+
icon = "TwelveLabs"
|
37
|
+
name = "TwelveLabsPegasusIndexVideo"
|
38
|
+
documentation = "https://github.com/twelvelabs-io/twelvelabs-developer-experience/blob/main/integrations/Langflow/TWELVE_LABS_COMPONENTS_README.md"
|
39
|
+
|
40
|
+
inputs = [
|
41
|
+
DataInput(
|
42
|
+
name="videodata",
|
43
|
+
display_name="Video Data",
|
44
|
+
info="Video Data objects (from VideoFile or SplitVideo)",
|
45
|
+
is_list=True,
|
46
|
+
required=True,
|
47
|
+
),
|
48
|
+
SecretStrInput(
|
49
|
+
name="api_key", display_name="TwelveLabs API Key", info="Enter your TwelveLabs API Key.", required=True
|
50
|
+
),
|
51
|
+
DropdownInput(
|
52
|
+
name="model_name",
|
53
|
+
display_name="Model",
|
54
|
+
info="Pegasus model to use for indexing",
|
55
|
+
options=["pegasus1.2"],
|
56
|
+
value="pegasus1.2",
|
57
|
+
advanced=False,
|
58
|
+
),
|
59
|
+
StrInput(
|
60
|
+
name="index_name",
|
61
|
+
display_name="Index Name",
|
62
|
+
info="Name of the index to use. If the index doesn't exist, it will be created.",
|
63
|
+
required=False,
|
64
|
+
),
|
65
|
+
StrInput(
|
66
|
+
name="index_id",
|
67
|
+
display_name="Index ID",
|
68
|
+
info="ID of an existing index to use. If provided, index_name will be ignored.",
|
69
|
+
required=False,
|
70
|
+
),
|
71
|
+
]
|
72
|
+
|
73
|
+
outputs = [
|
74
|
+
Output(
|
75
|
+
display_name="Indexed Data", name="indexed_data", method="index_videos", output_types=["Data"], is_list=True
|
76
|
+
),
|
77
|
+
]
|
78
|
+
|
79
|
+
def _get_or_create_index(self, client: TwelveLabs) -> tuple[str, str]:
|
80
|
+
"""Get existing index or create new one.
|
81
|
+
|
82
|
+
Returns (index_id, index_name).
|
83
|
+
"""
|
84
|
+
# First check if index_id is provided and valid
|
85
|
+
if hasattr(self, "index_id") and self.index_id:
|
86
|
+
try:
|
87
|
+
index = client.index.retrieve(id=self.index_id)
|
88
|
+
except (ValueError, KeyError) as e:
|
89
|
+
if not hasattr(self, "index_name") or not self.index_name:
|
90
|
+
error_msg = "Invalid index ID provided and no index name specified for fallback"
|
91
|
+
raise IndexCreationError(error_msg) from e
|
92
|
+
else:
|
93
|
+
return self.index_id, index.name
|
94
|
+
|
95
|
+
# If index_name is provided, try to find it
|
96
|
+
if hasattr(self, "index_name") and self.index_name:
|
97
|
+
try:
|
98
|
+
# List all indexes and find by name
|
99
|
+
indexes = client.index.list()
|
100
|
+
for idx in indexes:
|
101
|
+
if idx.name == self.index_name:
|
102
|
+
return idx.id, idx.name
|
103
|
+
|
104
|
+
# If we get here, index wasn't found - create it
|
105
|
+
index = client.index.create(
|
106
|
+
name=self.index_name,
|
107
|
+
models=[
|
108
|
+
{
|
109
|
+
"name": self.model_name if hasattr(self, "model_name") else "pegasus1.2",
|
110
|
+
"options": ["visual", "audio"],
|
111
|
+
}
|
112
|
+
],
|
113
|
+
)
|
114
|
+
except (ValueError, KeyError) as e:
|
115
|
+
error_msg = f"Error with index name {self.index_name}"
|
116
|
+
raise IndexCreationError(error_msg) from e
|
117
|
+
else:
|
118
|
+
return index.id, index.name
|
119
|
+
|
120
|
+
# If we get here, neither index_id nor index_name was provided
|
121
|
+
error_msg = "Either index_name or index_id must be provided"
|
122
|
+
raise IndexCreationError(error_msg)
|
123
|
+
|
124
|
+
def on_task_update(self, task: Any, video_path: str) -> None:
|
125
|
+
"""Callback for task status updates.
|
126
|
+
|
127
|
+
Updates the component status with the current task status.
|
128
|
+
"""
|
129
|
+
video_name = Path(video_path).name
|
130
|
+
status_msg = f"Indexing {video_name}... Status: {task.status}"
|
131
|
+
self.status = status_msg
|
132
|
+
|
133
|
+
@retry(stop=stop_after_attempt(5), wait=wait_exponential(multiplier=1, min=5, max=60), reraise=True)
|
134
|
+
def _check_task_status(
|
135
|
+
self,
|
136
|
+
client: TwelveLabs,
|
137
|
+
task_id: str,
|
138
|
+
video_path: str,
|
139
|
+
) -> Any:
|
140
|
+
"""Check task status once.
|
141
|
+
|
142
|
+
Makes a single API call to check the status of a task.
|
143
|
+
"""
|
144
|
+
task = client.task.retrieve(id=task_id)
|
145
|
+
self.on_task_update(task, video_path)
|
146
|
+
return task
|
147
|
+
|
148
|
+
def _wait_for_task_completion(
|
149
|
+
self, client: TwelveLabs, task_id: str, video_path: str, max_retries: int = 120, sleep_time: int = 10
|
150
|
+
) -> Any:
|
151
|
+
"""Wait for task completion with timeout and improved error handling.
|
152
|
+
|
153
|
+
Polls the task status until completion or timeout.
|
154
|
+
"""
|
155
|
+
retries = 0
|
156
|
+
consecutive_errors = 0
|
157
|
+
max_consecutive_errors = 5
|
158
|
+
video_name = Path(video_path).name
|
159
|
+
|
160
|
+
while retries < max_retries:
|
161
|
+
try:
|
162
|
+
self.status = f"Checking task status for {video_name} (attempt {retries + 1})"
|
163
|
+
task = self._check_task_status(client, task_id, video_path)
|
164
|
+
|
165
|
+
if task.status == "ready":
|
166
|
+
self.status = f"Indexing for {video_name} completed successfully!"
|
167
|
+
return task
|
168
|
+
if task.status == "failed":
|
169
|
+
error_msg = f"Task failed for {video_name}: {getattr(task, 'error', 'Unknown error')}"
|
170
|
+
self.status = error_msg
|
171
|
+
raise TaskError(error_msg)
|
172
|
+
if task.status == "error":
|
173
|
+
error_msg = f"Task encountered an error for {video_name}: {getattr(task, 'error', 'Unknown error')}"
|
174
|
+
self.status = error_msg
|
175
|
+
raise TaskError(error_msg)
|
176
|
+
|
177
|
+
time.sleep(sleep_time)
|
178
|
+
retries += 1
|
179
|
+
elapsed_time = retries * sleep_time
|
180
|
+
self.status = f"Indexing {video_name}... {elapsed_time}s elapsed"
|
181
|
+
|
182
|
+
except (ValueError, KeyError) as e:
|
183
|
+
consecutive_errors += 1
|
184
|
+
error_msg = f"Error checking task status for {video_name}: {e!s}"
|
185
|
+
self.status = error_msg
|
186
|
+
|
187
|
+
if consecutive_errors >= max_consecutive_errors:
|
188
|
+
too_many_errors = f"Too many consecutive errors checking task status for {video_name}"
|
189
|
+
raise TaskError(too_many_errors) from e
|
190
|
+
|
191
|
+
time.sleep(sleep_time * (2**consecutive_errors))
|
192
|
+
continue
|
193
|
+
|
194
|
+
timeout_msg = f"Timeout waiting for indexing of {video_name} after {max_retries * sleep_time} seconds"
|
195
|
+
self.status = timeout_msg
|
196
|
+
raise TaskTimeoutError(timeout_msg)
|
197
|
+
|
198
|
+
def _upload_video(self, client: TwelveLabs, video_path: str, index_id: str) -> str:
|
199
|
+
"""Upload a single video and return its task ID.
|
200
|
+
|
201
|
+
Uploads a video file to the specified index and returns the task ID.
|
202
|
+
"""
|
203
|
+
video_name = Path(video_path).name
|
204
|
+
with Path(video_path).open("rb") as video_file:
|
205
|
+
self.status = f"Uploading {video_name} to index {index_id}..."
|
206
|
+
task = client.task.create(index_id=index_id, file=video_file)
|
207
|
+
task_id = task.id
|
208
|
+
self.status = f"Upload complete for {video_name}. Task ID: {task_id}"
|
209
|
+
return task_id
|
210
|
+
|
211
|
+
def index_videos(self) -> list[Data]:
|
212
|
+
"""Indexes each video and adds the video_id to its metadata."""
|
213
|
+
if not self.videodata:
|
214
|
+
self.status = "No video data provided."
|
215
|
+
return []
|
216
|
+
|
217
|
+
if not self.api_key:
|
218
|
+
error_msg = "TwelveLabs API Key is required"
|
219
|
+
raise IndexCreationError(error_msg)
|
220
|
+
|
221
|
+
if not (hasattr(self, "index_name") and self.index_name) and not (hasattr(self, "index_id") and self.index_id):
|
222
|
+
error_msg = "Either index_name or index_id must be provided"
|
223
|
+
raise IndexCreationError(error_msg)
|
224
|
+
|
225
|
+
client = TwelveLabs(api_key=self.api_key)
|
226
|
+
indexed_data_list: list[Data] = []
|
227
|
+
|
228
|
+
# Get or create the index
|
229
|
+
try:
|
230
|
+
index_id, index_name = self._get_or_create_index(client)
|
231
|
+
self.status = f"Using index: {index_name} (ID: {index_id})"
|
232
|
+
except IndexCreationError as e:
|
233
|
+
self.status = f"Failed to get/create TwelveLabs index: {e!s}"
|
234
|
+
raise
|
235
|
+
|
236
|
+
# First, validate all videos and create a list of valid ones
|
237
|
+
valid_videos: list[tuple[Data, str]] = []
|
238
|
+
for video_data_item in self.videodata:
|
239
|
+
if not isinstance(video_data_item, Data):
|
240
|
+
self.status = f"Skipping invalid data item: {video_data_item}"
|
241
|
+
continue
|
242
|
+
|
243
|
+
video_info = video_data_item.data
|
244
|
+
if not isinstance(video_info, dict):
|
245
|
+
self.status = f"Skipping item with invalid data structure: {video_info}"
|
246
|
+
continue
|
247
|
+
|
248
|
+
video_path = video_info.get("text")
|
249
|
+
if not video_path or not isinstance(video_path, str):
|
250
|
+
self.status = f"Skipping item with missing or invalid video path: {video_info}"
|
251
|
+
continue
|
252
|
+
|
253
|
+
if not Path(video_path).exists():
|
254
|
+
self.status = f"Video file not found, skipping: {video_path}"
|
255
|
+
continue
|
256
|
+
|
257
|
+
valid_videos.append((video_data_item, video_path))
|
258
|
+
|
259
|
+
if not valid_videos:
|
260
|
+
self.status = "No valid videos to process."
|
261
|
+
return []
|
262
|
+
|
263
|
+
# Upload all videos first and collect their task IDs
|
264
|
+
upload_tasks: list[tuple[Data, str, str]] = [] # (data_item, video_path, task_id)
|
265
|
+
for data_item, video_path in valid_videos:
|
266
|
+
try:
|
267
|
+
task_id = self._upload_video(client, video_path, index_id)
|
268
|
+
upload_tasks.append((data_item, video_path, task_id))
|
269
|
+
except (ValueError, KeyError) as e:
|
270
|
+
self.status = f"Failed to upload {video_path}: {e!s}"
|
271
|
+
continue
|
272
|
+
|
273
|
+
# Now check all tasks in parallel using a thread pool
|
274
|
+
with ThreadPoolExecutor(max_workers=min(10, len(upload_tasks))) as executor:
|
275
|
+
futures = []
|
276
|
+
for data_item, video_path, task_id in upload_tasks:
|
277
|
+
future = executor.submit(self._wait_for_task_completion, client, task_id, video_path)
|
278
|
+
futures.append((data_item, video_path, future))
|
279
|
+
|
280
|
+
# Process results as they complete
|
281
|
+
for data_item, video_path, future in futures:
|
282
|
+
try:
|
283
|
+
completed_task = future.result()
|
284
|
+
if completed_task.status == "ready":
|
285
|
+
video_id = completed_task.video_id
|
286
|
+
video_name = Path(video_path).name
|
287
|
+
self.status = f"Video {video_name} indexed successfully. Video ID: {video_id}"
|
288
|
+
|
289
|
+
# Add video_id to the metadata
|
290
|
+
video_info = data_item.data
|
291
|
+
if "metadata" not in video_info:
|
292
|
+
video_info["metadata"] = {}
|
293
|
+
elif not isinstance(video_info["metadata"], dict):
|
294
|
+
self.status = f"Warning: Overwriting non-dict metadata for {video_path}"
|
295
|
+
video_info["metadata"] = {}
|
296
|
+
|
297
|
+
video_info["metadata"].update(
|
298
|
+
{"video_id": video_id, "index_id": index_id, "index_name": index_name}
|
299
|
+
)
|
300
|
+
|
301
|
+
updated_data_item = Data(data=video_info)
|
302
|
+
indexed_data_list.append(updated_data_item)
|
303
|
+
except (TaskError, TaskTimeoutError) as e:
|
304
|
+
self.status = f"Failed to process {video_path}: {e!s}"
|
305
|
+
|
306
|
+
if not indexed_data_list:
|
307
|
+
self.status = "No videos were successfully indexed."
|
308
|
+
else:
|
309
|
+
self.status = f"Finished indexing {len(indexed_data_list)}/{len(self.videodata)} videos."
|
310
|
+
|
311
|
+
return indexed_data_list
|
@@ -0,0 +1,291 @@
|
|
1
|
+
import hashlib
|
2
|
+
import math
|
3
|
+
import subprocess
|
4
|
+
from datetime import datetime, timezone
|
5
|
+
from pathlib import Path
|
6
|
+
from typing import Any
|
7
|
+
|
8
|
+
from lfx.custom import Component
|
9
|
+
from lfx.inputs import BoolInput, DropdownInput, HandleInput, IntInput
|
10
|
+
from lfx.schema import Data
|
11
|
+
from lfx.template import Output
|
12
|
+
|
13
|
+
|
14
|
+
class SplitVideoComponent(Component):
|
15
|
+
"""A component that splits a video into multiple clips of specified duration using FFmpeg."""
|
16
|
+
|
17
|
+
display_name = "Split Video"
|
18
|
+
description = "Split a video into multiple clips of specified duration."
|
19
|
+
icon = "TwelveLabs"
|
20
|
+
name = "SplitVideo"
|
21
|
+
documentation = "https://github.com/twelvelabs-io/twelvelabs-developer-experience/blob/main/integrations/Langflow/TWELVE_LABS_COMPONENTS_README.md"
|
22
|
+
|
23
|
+
inputs = [
|
24
|
+
HandleInput(
|
25
|
+
name="videodata",
|
26
|
+
display_name="Video Data",
|
27
|
+
info="Input video data from VideoFile component",
|
28
|
+
required=True,
|
29
|
+
input_types=["Data"],
|
30
|
+
),
|
31
|
+
IntInput(
|
32
|
+
name="clip_duration",
|
33
|
+
display_name="Clip Duration (seconds)",
|
34
|
+
info="Duration of each clip in seconds",
|
35
|
+
required=True,
|
36
|
+
value=30,
|
37
|
+
),
|
38
|
+
DropdownInput(
|
39
|
+
name="last_clip_handling",
|
40
|
+
display_name="Last Clip Handling",
|
41
|
+
info=(
|
42
|
+
"How to handle the final clip when it would be shorter than the specified duration:\n"
|
43
|
+
"- Truncate: Skip the final clip entirely if it's shorter than the specified duration\n"
|
44
|
+
"- Overlap Previous: Start the final clip earlier to maintain full duration, "
|
45
|
+
"overlapping with previous clip\n"
|
46
|
+
"- Keep Short: Keep the final clip at its natural length, even if shorter than specified duration"
|
47
|
+
),
|
48
|
+
options=["Truncate", "Overlap Previous", "Keep Short"],
|
49
|
+
value="Overlap Previous",
|
50
|
+
required=True,
|
51
|
+
),
|
52
|
+
BoolInput(
|
53
|
+
name="include_original",
|
54
|
+
display_name="Include Original Video",
|
55
|
+
info="Whether to include the original video in the output",
|
56
|
+
value=False,
|
57
|
+
),
|
58
|
+
]
|
59
|
+
|
60
|
+
outputs = [
|
61
|
+
Output(
|
62
|
+
name="clips",
|
63
|
+
display_name="Video Clips",
|
64
|
+
method="process",
|
65
|
+
output_types=["Data"],
|
66
|
+
),
|
67
|
+
]
|
68
|
+
|
69
|
+
def get_video_duration(self, video_path: str) -> float:
|
70
|
+
"""Get video duration using FFmpeg."""
|
71
|
+
try:
|
72
|
+
# Validate video path to prevent shell injection
|
73
|
+
if not isinstance(video_path, str) or any(c in video_path for c in ";&|`$(){}[]<>*?!#~"):
|
74
|
+
error_msg = "Invalid video path"
|
75
|
+
raise ValueError(error_msg)
|
76
|
+
|
77
|
+
cmd = [
|
78
|
+
"ffprobe",
|
79
|
+
"-v",
|
80
|
+
"error",
|
81
|
+
"-show_entries",
|
82
|
+
"format=duration",
|
83
|
+
"-of",
|
84
|
+
"default=noprint_wrappers=1:nokey=1",
|
85
|
+
video_path,
|
86
|
+
]
|
87
|
+
result = subprocess.run( # noqa: S603
|
88
|
+
cmd,
|
89
|
+
capture_output=True,
|
90
|
+
text=True,
|
91
|
+
check=False,
|
92
|
+
shell=False, # Explicitly set shell=False for security
|
93
|
+
)
|
94
|
+
if result.returncode != 0:
|
95
|
+
error_msg = f"FFprobe error: {result.stderr}"
|
96
|
+
raise RuntimeError(error_msg)
|
97
|
+
return float(result.stdout.strip())
|
98
|
+
except Exception as e:
|
99
|
+
self.log(f"Error getting video duration: {e!s}", "ERROR")
|
100
|
+
raise
|
101
|
+
|
102
|
+
def get_output_dir(self, video_path: str) -> str:
|
103
|
+
"""Create a unique output directory for clips based on video name and timestamp."""
|
104
|
+
# Get the video filename without extension
|
105
|
+
path_obj = Path(video_path)
|
106
|
+
base_name = path_obj.stem
|
107
|
+
|
108
|
+
# Create a timestamp
|
109
|
+
timestamp = datetime.now(tz=timezone.utc).strftime("%Y-%m-%d_%H-%M-%S")
|
110
|
+
|
111
|
+
# Create a unique hash from the video path
|
112
|
+
path_hash = hashlib.sha256(video_path.encode()).hexdigest()[:8]
|
113
|
+
|
114
|
+
# Create the output directory path
|
115
|
+
output_dir = Path(path_obj.parent) / f"clips_{base_name}_{timestamp}_{path_hash}"
|
116
|
+
|
117
|
+
# Create the directory if it doesn't exist
|
118
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
119
|
+
|
120
|
+
return str(output_dir)
|
121
|
+
|
122
|
+
def process_video(self, video_path: str, clip_duration: int, *, include_original: bool) -> list[Data]:
|
123
|
+
"""Process video and split it into clips using FFmpeg."""
|
124
|
+
try:
|
125
|
+
# Get video duration
|
126
|
+
total_duration = self.get_video_duration(video_path)
|
127
|
+
|
128
|
+
# Calculate number of clips (ceiling to include partial clip)
|
129
|
+
num_clips = math.ceil(total_duration / clip_duration)
|
130
|
+
self.log(
|
131
|
+
f"Total duration: {total_duration}s, Clip duration: {clip_duration}s, Number of clips: {num_clips}"
|
132
|
+
)
|
133
|
+
|
134
|
+
# Create output directory for clips
|
135
|
+
output_dir = self.get_output_dir(video_path)
|
136
|
+
|
137
|
+
# Get original video info
|
138
|
+
path_obj = Path(video_path)
|
139
|
+
original_filename = path_obj.name
|
140
|
+
original_name = path_obj.stem
|
141
|
+
|
142
|
+
# List to store all video paths (including original if requested)
|
143
|
+
video_paths: list[Data] = []
|
144
|
+
|
145
|
+
# Add original video if requested
|
146
|
+
if include_original:
|
147
|
+
original_data: dict[str, Any] = {
|
148
|
+
"text": video_path,
|
149
|
+
"metadata": {
|
150
|
+
"source": video_path,
|
151
|
+
"type": "video",
|
152
|
+
"clip_index": -1, # -1 indicates original video
|
153
|
+
"duration": int(total_duration), # Convert to int
|
154
|
+
"original_video": {
|
155
|
+
"name": original_name,
|
156
|
+
"filename": original_filename,
|
157
|
+
"path": video_path,
|
158
|
+
"duration": int(total_duration), # Convert to int
|
159
|
+
"total_clips": int(num_clips),
|
160
|
+
"clip_duration": int(clip_duration),
|
161
|
+
},
|
162
|
+
},
|
163
|
+
}
|
164
|
+
video_paths.append(Data(data=original_data))
|
165
|
+
|
166
|
+
# Split video into clips
|
167
|
+
for i in range(int(num_clips)): # Convert num_clips to int for range
|
168
|
+
start_time = float(i * clip_duration) # Convert to float for time calculations
|
169
|
+
end_time = min(float((i + 1) * clip_duration), total_duration)
|
170
|
+
duration = end_time - start_time
|
171
|
+
|
172
|
+
# Handle last clip if it's shorter
|
173
|
+
if i == int(num_clips) - 1 and duration < clip_duration: # Convert num_clips to int for comparison
|
174
|
+
if self.last_clip_handling == "Truncate":
|
175
|
+
# Skip if the last clip would be too short
|
176
|
+
continue
|
177
|
+
if self.last_clip_handling == "Overlap Previous" and i > 0:
|
178
|
+
# Start from earlier to make full duration
|
179
|
+
start_time = total_duration - clip_duration
|
180
|
+
duration = clip_duration
|
181
|
+
# For "Keep Short", we use the original start_time and duration
|
182
|
+
|
183
|
+
# Skip if duration is too small (less than 1 second)
|
184
|
+
if duration < 1:
|
185
|
+
continue
|
186
|
+
|
187
|
+
# Generate output path
|
188
|
+
output_path = Path(output_dir) / f"clip_{i:03d}.mp4"
|
189
|
+
output_path_str = str(output_path)
|
190
|
+
|
191
|
+
try:
|
192
|
+
# Use FFmpeg to split the video
|
193
|
+
cmd = [
|
194
|
+
"ffmpeg",
|
195
|
+
"-i",
|
196
|
+
video_path,
|
197
|
+
"-ss",
|
198
|
+
str(start_time),
|
199
|
+
"-t",
|
200
|
+
str(duration),
|
201
|
+
"-c:v",
|
202
|
+
"libx264",
|
203
|
+
"-c:a",
|
204
|
+
"aac",
|
205
|
+
"-y", # Overwrite output file if it exists
|
206
|
+
output_path_str,
|
207
|
+
]
|
208
|
+
|
209
|
+
result = subprocess.run( # noqa: S603
|
210
|
+
cmd,
|
211
|
+
capture_output=True,
|
212
|
+
text=True,
|
213
|
+
check=False,
|
214
|
+
shell=False, # Explicitly set shell=False for security
|
215
|
+
)
|
216
|
+
if result.returncode != 0:
|
217
|
+
error_msg = f"FFmpeg error: {result.stderr}"
|
218
|
+
raise RuntimeError(error_msg)
|
219
|
+
|
220
|
+
# Create timestamp string for metadata
|
221
|
+
start_min = int(start_time // 60)
|
222
|
+
start_sec = int(start_time % 60)
|
223
|
+
end_min = int(end_time // 60)
|
224
|
+
end_sec = int(end_time % 60)
|
225
|
+
timestamp_str = f"{start_min:02d}:{start_sec:02d} - {end_min:02d}:{end_sec:02d}"
|
226
|
+
|
227
|
+
# Create Data object for the clip
|
228
|
+
clip_data: dict[str, Any] = {
|
229
|
+
"text": output_path_str,
|
230
|
+
"metadata": {
|
231
|
+
"source": video_path,
|
232
|
+
"type": "video",
|
233
|
+
"clip_index": i,
|
234
|
+
"start_time": float(start_time),
|
235
|
+
"end_time": float(end_time),
|
236
|
+
"duration": float(duration),
|
237
|
+
"original_video": {
|
238
|
+
"name": original_name,
|
239
|
+
"filename": original_filename,
|
240
|
+
"path": video_path,
|
241
|
+
"duration": int(total_duration),
|
242
|
+
"total_clips": int(num_clips),
|
243
|
+
"clip_duration": int(clip_duration),
|
244
|
+
},
|
245
|
+
"clip": {
|
246
|
+
"index": i,
|
247
|
+
"total": int(num_clips),
|
248
|
+
"duration": float(duration),
|
249
|
+
"start_time": float(start_time),
|
250
|
+
"end_time": float(end_time),
|
251
|
+
"timestamp": timestamp_str,
|
252
|
+
},
|
253
|
+
},
|
254
|
+
}
|
255
|
+
video_paths.append(Data(data=clip_data))
|
256
|
+
|
257
|
+
except Exception as e:
|
258
|
+
self.log(f"Error processing clip {i}: {e!s}", "ERROR")
|
259
|
+
raise
|
260
|
+
|
261
|
+
self.log(f"Created {len(video_paths)} clips in {output_dir}")
|
262
|
+
except Exception as e:
|
263
|
+
self.log(f"Error processing video: {e!s}", "ERROR")
|
264
|
+
raise
|
265
|
+
else:
|
266
|
+
return video_paths
|
267
|
+
|
268
|
+
def process(self) -> list[Data]:
|
269
|
+
"""Process the input video and return a list of Data objects containing the clips."""
|
270
|
+
try:
|
271
|
+
# Get the input video path from the previous component
|
272
|
+
if not hasattr(self, "videodata") or not isinstance(self.videodata, list) or len(self.videodata) != 1:
|
273
|
+
error_msg = "Please provide exactly one video"
|
274
|
+
raise ValueError(error_msg)
|
275
|
+
|
276
|
+
video_path = self.videodata[0].data.get("text")
|
277
|
+
if not video_path or not Path(video_path).exists():
|
278
|
+
error_msg = "Invalid video path"
|
279
|
+
raise ValueError(error_msg)
|
280
|
+
|
281
|
+
# Validate video path to prevent shell injection
|
282
|
+
if not isinstance(video_path, str) or any(c in video_path for c in ";&|`$(){}[]<>*?!#~"):
|
283
|
+
error_msg = "Invalid video path contains unsafe characters"
|
284
|
+
raise ValueError(error_msg)
|
285
|
+
|
286
|
+
# Process the video
|
287
|
+
return self.process_video(video_path, self.clip_duration, include_original=self.include_original)
|
288
|
+
|
289
|
+
except Exception as e:
|
290
|
+
self.log(f"Error in split video component: {e!s}", "ERROR")
|
291
|
+
raise
|
@@ -0,0 +1,57 @@
|
|
1
|
+
from twelvelabs import TwelveLabs
|
2
|
+
|
3
|
+
from lfx.base.embeddings.model import LCEmbeddingsModel
|
4
|
+
from lfx.field_typing import Embeddings
|
5
|
+
from lfx.io import DropdownInput, FloatInput, IntInput, SecretStrInput
|
6
|
+
|
7
|
+
|
8
|
+
class TwelveLabsTextEmbeddings(Embeddings):
|
9
|
+
def __init__(self, api_key: str, model: str) -> None:
|
10
|
+
self.client = TwelveLabs(api_key=api_key)
|
11
|
+
self.model = model
|
12
|
+
|
13
|
+
def embed_documents(self, texts: list[str]) -> list[list[float]]:
|
14
|
+
all_embeddings: list[list[float]] = []
|
15
|
+
for text in texts:
|
16
|
+
if not text:
|
17
|
+
continue
|
18
|
+
|
19
|
+
result = self.client.embed.create(model_name=self.model, text=text)
|
20
|
+
|
21
|
+
if result.text_embedding and result.text_embedding.segments:
|
22
|
+
for segment in result.text_embedding.segments:
|
23
|
+
all_embeddings.append([float(x) for x in segment.embeddings_float])
|
24
|
+
break # Only take first segment for now
|
25
|
+
|
26
|
+
return all_embeddings
|
27
|
+
|
28
|
+
def embed_query(self, text: str) -> list[float]:
|
29
|
+
result = self.client.embed.create(model_name=self.model, text=text)
|
30
|
+
|
31
|
+
if result.text_embedding and result.text_embedding.segments:
|
32
|
+
return [float(x) for x in result.text_embedding.segments[0].embeddings_float]
|
33
|
+
return []
|
34
|
+
|
35
|
+
|
36
|
+
class TwelveLabsTextEmbeddingsComponent(LCEmbeddingsModel):
|
37
|
+
display_name = "TwelveLabs Text Embeddings"
|
38
|
+
description = "Generate embeddings using TwelveLabs text embedding models."
|
39
|
+
icon = "TwelveLabs"
|
40
|
+
name = "TwelveLabsTextEmbeddings"
|
41
|
+
documentation = "https://github.com/twelvelabs-io/twelvelabs-developer-experience/blob/main/integrations/Langflow/TWELVE_LABS_COMPONENTS_README.md"
|
42
|
+
|
43
|
+
inputs = [
|
44
|
+
SecretStrInput(name="api_key", display_name="TwelveLabs API Key", value="TWELVELABS_API_KEY", required=True),
|
45
|
+
DropdownInput(
|
46
|
+
name="model",
|
47
|
+
display_name="Model",
|
48
|
+
advanced=False,
|
49
|
+
options=["Marengo-retrieval-2.7"],
|
50
|
+
value="Marengo-retrieval-2.7",
|
51
|
+
),
|
52
|
+
IntInput(name="max_retries", display_name="Max Retries", value=3, advanced=True),
|
53
|
+
FloatInput(name="request_timeout", display_name="Request Timeout", advanced=True),
|
54
|
+
]
|
55
|
+
|
56
|
+
def build_embeddings(self) -> Embeddings:
|
57
|
+
return TwelveLabsTextEmbeddings(api_key=self.api_key, model=self.model)
|