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,231 @@
|
|
1
|
+
from contextlib import contextmanager
|
2
|
+
|
3
|
+
import pandas as pd
|
4
|
+
from googleapiclient.discovery import build
|
5
|
+
from googleapiclient.errors import HttpError
|
6
|
+
|
7
|
+
from lfx.custom.custom_component.component import Component
|
8
|
+
from lfx.inputs.inputs import BoolInput, DropdownInput, IntInput, MessageTextInput, SecretStrInput
|
9
|
+
from lfx.schema.dataframe import DataFrame
|
10
|
+
from lfx.template.field.base import Output
|
11
|
+
|
12
|
+
|
13
|
+
class YouTubeCommentsComponent(Component):
|
14
|
+
"""A component that retrieves comments from YouTube videos."""
|
15
|
+
|
16
|
+
display_name: str = "YouTube Comments"
|
17
|
+
description: str = "Retrieves and analyzes comments from YouTube videos."
|
18
|
+
icon: str = "YouTube"
|
19
|
+
|
20
|
+
# Constants
|
21
|
+
COMMENTS_DISABLED_STATUS = 403
|
22
|
+
NOT_FOUND_STATUS = 404
|
23
|
+
API_MAX_RESULTS = 100
|
24
|
+
|
25
|
+
inputs = [
|
26
|
+
MessageTextInput(
|
27
|
+
name="video_url",
|
28
|
+
display_name="Video URL",
|
29
|
+
info="The URL of the YouTube video to get comments from.",
|
30
|
+
tool_mode=True,
|
31
|
+
required=True,
|
32
|
+
),
|
33
|
+
SecretStrInput(
|
34
|
+
name="api_key",
|
35
|
+
display_name="YouTube API Key",
|
36
|
+
info="Your YouTube Data API key.",
|
37
|
+
required=True,
|
38
|
+
),
|
39
|
+
IntInput(
|
40
|
+
name="max_results",
|
41
|
+
display_name="Max Results",
|
42
|
+
value=20,
|
43
|
+
info="The maximum number of comments to return.",
|
44
|
+
),
|
45
|
+
DropdownInput(
|
46
|
+
name="sort_by",
|
47
|
+
display_name="Sort By",
|
48
|
+
options=["time", "relevance"],
|
49
|
+
value="relevance",
|
50
|
+
info="Sort comments by time or relevance.",
|
51
|
+
),
|
52
|
+
BoolInput(
|
53
|
+
name="include_replies",
|
54
|
+
display_name="Include Replies",
|
55
|
+
value=False,
|
56
|
+
info="Whether to include replies to comments.",
|
57
|
+
advanced=True,
|
58
|
+
),
|
59
|
+
BoolInput(
|
60
|
+
name="include_metrics",
|
61
|
+
display_name="Include Metrics",
|
62
|
+
value=True,
|
63
|
+
info="Include metrics like like count and reply count.",
|
64
|
+
advanced=True,
|
65
|
+
),
|
66
|
+
]
|
67
|
+
|
68
|
+
outputs = [
|
69
|
+
Output(name="comments", display_name="Comments", method="get_video_comments"),
|
70
|
+
]
|
71
|
+
|
72
|
+
def _extract_video_id(self, video_url: str) -> str:
|
73
|
+
"""Extracts the video ID from a YouTube URL."""
|
74
|
+
import re
|
75
|
+
|
76
|
+
patterns = [
|
77
|
+
r"(?:youtube\.com\/watch\?v=|youtu.be\/|youtube.com\/embed\/)([^&\n?#]+)",
|
78
|
+
r"youtube.com\/shorts\/([^&\n?#]+)",
|
79
|
+
]
|
80
|
+
|
81
|
+
for pattern in patterns:
|
82
|
+
match = re.search(pattern, video_url)
|
83
|
+
if match:
|
84
|
+
return match.group(1)
|
85
|
+
|
86
|
+
return video_url.strip()
|
87
|
+
|
88
|
+
def _process_reply(self, reply: dict, parent_id: str, *, include_metrics: bool = True) -> dict:
|
89
|
+
"""Process a single reply comment."""
|
90
|
+
reply_snippet = reply["snippet"]
|
91
|
+
reply_data = {
|
92
|
+
"comment_id": reply["id"],
|
93
|
+
"parent_comment_id": parent_id,
|
94
|
+
"author": reply_snippet["authorDisplayName"],
|
95
|
+
"text": reply_snippet["textDisplay"],
|
96
|
+
"published_at": reply_snippet["publishedAt"],
|
97
|
+
"is_reply": True,
|
98
|
+
}
|
99
|
+
if include_metrics:
|
100
|
+
reply_data["like_count"] = reply_snippet["likeCount"]
|
101
|
+
reply_data["reply_count"] = 0 # Replies can't have replies
|
102
|
+
|
103
|
+
return reply_data
|
104
|
+
|
105
|
+
def _process_comment(
|
106
|
+
self, item: dict, *, include_metrics: bool = True, include_replies: bool = False
|
107
|
+
) -> list[dict]:
|
108
|
+
"""Process a single comment thread."""
|
109
|
+
comment = item["snippet"]["topLevelComment"]["snippet"]
|
110
|
+
comment_id = item["snippet"]["topLevelComment"]["id"]
|
111
|
+
|
112
|
+
# Basic comment data
|
113
|
+
processed_comments = [
|
114
|
+
{
|
115
|
+
"comment_id": comment_id,
|
116
|
+
"parent_comment_id": "", # Empty for top-level comments
|
117
|
+
"author": comment["authorDisplayName"],
|
118
|
+
"author_channel_url": comment.get("authorChannelUrl", ""),
|
119
|
+
"text": comment["textDisplay"],
|
120
|
+
"published_at": comment["publishedAt"],
|
121
|
+
"updated_at": comment["updatedAt"],
|
122
|
+
"is_reply": False,
|
123
|
+
}
|
124
|
+
]
|
125
|
+
|
126
|
+
# Add metrics if requested
|
127
|
+
if include_metrics:
|
128
|
+
processed_comments[0].update(
|
129
|
+
{
|
130
|
+
"like_count": comment["likeCount"],
|
131
|
+
"reply_count": item["snippet"]["totalReplyCount"],
|
132
|
+
}
|
133
|
+
)
|
134
|
+
|
135
|
+
# Add replies if requested
|
136
|
+
if include_replies and item["snippet"]["totalReplyCount"] > 0 and "replies" in item:
|
137
|
+
for reply in item["replies"]["comments"]:
|
138
|
+
reply_data = self._process_reply(reply, parent_id=comment_id, include_metrics=include_metrics)
|
139
|
+
processed_comments.append(reply_data)
|
140
|
+
|
141
|
+
return processed_comments
|
142
|
+
|
143
|
+
@contextmanager
|
144
|
+
def youtube_client(self):
|
145
|
+
"""Context manager for YouTube API client."""
|
146
|
+
client = build("youtube", "v3", developerKey=self.api_key)
|
147
|
+
try:
|
148
|
+
yield client
|
149
|
+
finally:
|
150
|
+
client.close()
|
151
|
+
|
152
|
+
def get_video_comments(self) -> DataFrame:
|
153
|
+
"""Retrieves comments from a YouTube video and returns as DataFrame."""
|
154
|
+
try:
|
155
|
+
# Extract video ID from URL
|
156
|
+
video_id = self._extract_video_id(self.video_url)
|
157
|
+
|
158
|
+
# Use context manager for YouTube API client
|
159
|
+
with self.youtube_client() as youtube:
|
160
|
+
comments_data = []
|
161
|
+
results_count = 0
|
162
|
+
request = youtube.commentThreads().list(
|
163
|
+
part="snippet,replies",
|
164
|
+
videoId=video_id,
|
165
|
+
maxResults=min(self.API_MAX_RESULTS, self.max_results),
|
166
|
+
order=self.sort_by,
|
167
|
+
textFormat="plainText",
|
168
|
+
)
|
169
|
+
|
170
|
+
while request and results_count < self.max_results:
|
171
|
+
response = request.execute()
|
172
|
+
|
173
|
+
for item in response.get("items", []):
|
174
|
+
if results_count >= self.max_results:
|
175
|
+
break
|
176
|
+
|
177
|
+
comments = self._process_comment(
|
178
|
+
item, include_metrics=self.include_metrics, include_replies=self.include_replies
|
179
|
+
)
|
180
|
+
comments_data.extend(comments)
|
181
|
+
results_count += 1
|
182
|
+
|
183
|
+
# Get the next page if available and needed
|
184
|
+
if "nextPageToken" in response and results_count < self.max_results:
|
185
|
+
request = youtube.commentThreads().list(
|
186
|
+
part="snippet,replies",
|
187
|
+
videoId=video_id,
|
188
|
+
maxResults=min(self.API_MAX_RESULTS, self.max_results - results_count),
|
189
|
+
order=self.sort_by,
|
190
|
+
textFormat="plainText",
|
191
|
+
pageToken=response["nextPageToken"],
|
192
|
+
)
|
193
|
+
else:
|
194
|
+
request = None
|
195
|
+
|
196
|
+
# Convert to DataFrame
|
197
|
+
comments_df = pd.DataFrame(comments_data)
|
198
|
+
|
199
|
+
# Add video metadata
|
200
|
+
comments_df["video_id"] = video_id
|
201
|
+
comments_df["video_url"] = self.video_url
|
202
|
+
|
203
|
+
# Sort columns for better organization
|
204
|
+
column_order = [
|
205
|
+
"video_id",
|
206
|
+
"video_url",
|
207
|
+
"comment_id",
|
208
|
+
"parent_comment_id",
|
209
|
+
"is_reply",
|
210
|
+
"author",
|
211
|
+
"author_channel_url",
|
212
|
+
"text",
|
213
|
+
"published_at",
|
214
|
+
"updated_at",
|
215
|
+
]
|
216
|
+
|
217
|
+
if self.include_metrics:
|
218
|
+
column_order.extend(["like_count", "reply_count"])
|
219
|
+
|
220
|
+
comments_df = comments_df[column_order]
|
221
|
+
|
222
|
+
return DataFrame(comments_df)
|
223
|
+
|
224
|
+
except HttpError as e:
|
225
|
+
error_message = f"YouTube API error: {e!s}"
|
226
|
+
if e.resp.status == self.COMMENTS_DISABLED_STATUS:
|
227
|
+
error_message = "Comments are disabled for this video or API quota exceeded."
|
228
|
+
elif e.resp.status == self.NOT_FOUND_STATUS:
|
229
|
+
error_message = "Video not found."
|
230
|
+
|
231
|
+
return DataFrame(pd.DataFrame({"error": [error_message]}))
|
@@ -0,0 +1,33 @@
|
|
1
|
+
from pytube import Playlist # Ensure you have pytube installed
|
2
|
+
|
3
|
+
from lfx.custom.custom_component.component import Component
|
4
|
+
from lfx.inputs.inputs import MessageTextInput
|
5
|
+
from lfx.schema.data import Data
|
6
|
+
from lfx.schema.dataframe import DataFrame
|
7
|
+
from lfx.template.field.base import Output
|
8
|
+
|
9
|
+
|
10
|
+
class YouTubePlaylistComponent(Component):
|
11
|
+
display_name = "YouTube Playlist"
|
12
|
+
description = "Extracts all video URLs from a YouTube playlist."
|
13
|
+
icon = "YouTube" # Replace with a suitable icon
|
14
|
+
|
15
|
+
inputs = [
|
16
|
+
MessageTextInput(
|
17
|
+
name="playlist_url",
|
18
|
+
display_name="Playlist URL",
|
19
|
+
info="URL of the YouTube playlist.",
|
20
|
+
required=True,
|
21
|
+
),
|
22
|
+
]
|
23
|
+
|
24
|
+
outputs = [
|
25
|
+
Output(display_name="Video URLs", name="video_urls", method="extract_video_urls"),
|
26
|
+
]
|
27
|
+
|
28
|
+
def extract_video_urls(self) -> DataFrame:
|
29
|
+
playlist_url = self.playlist_url
|
30
|
+
playlist = Playlist(playlist_url)
|
31
|
+
video_urls = [video.watch_url for video in playlist.videos]
|
32
|
+
|
33
|
+
return DataFrame([Data(data={"video_url": url}) for url in video_urls])
|
@@ -0,0 +1,120 @@
|
|
1
|
+
from contextlib import contextmanager
|
2
|
+
|
3
|
+
import pandas as pd
|
4
|
+
from googleapiclient.discovery import build
|
5
|
+
from googleapiclient.errors import HttpError
|
6
|
+
|
7
|
+
from lfx.custom.custom_component.component import Component
|
8
|
+
from lfx.inputs.inputs import BoolInput, DropdownInput, IntInput, MessageTextInput, SecretStrInput
|
9
|
+
from lfx.schema.dataframe import DataFrame
|
10
|
+
from lfx.template.field.base import Output
|
11
|
+
|
12
|
+
|
13
|
+
class YouTubeSearchComponent(Component):
|
14
|
+
"""A component that searches YouTube videos."""
|
15
|
+
|
16
|
+
display_name: str = "YouTube Search"
|
17
|
+
description: str = "Searches YouTube videos based on query."
|
18
|
+
icon: str = "YouTube"
|
19
|
+
|
20
|
+
inputs = [
|
21
|
+
MessageTextInput(
|
22
|
+
name="query",
|
23
|
+
display_name="Search Query",
|
24
|
+
info="The search query to look for on YouTube.",
|
25
|
+
tool_mode=True,
|
26
|
+
required=True,
|
27
|
+
),
|
28
|
+
SecretStrInput(
|
29
|
+
name="api_key",
|
30
|
+
display_name="YouTube API Key",
|
31
|
+
info="Your YouTube Data API key.",
|
32
|
+
required=True,
|
33
|
+
),
|
34
|
+
IntInput(
|
35
|
+
name="max_results",
|
36
|
+
display_name="Max Results",
|
37
|
+
value=10,
|
38
|
+
info="The maximum number of results to return.",
|
39
|
+
),
|
40
|
+
DropdownInput(
|
41
|
+
name="order",
|
42
|
+
display_name="Sort Order",
|
43
|
+
options=["relevance", "date", "rating", "title", "viewCount"],
|
44
|
+
value="relevance",
|
45
|
+
info="Sort order for the search results.",
|
46
|
+
),
|
47
|
+
BoolInput(
|
48
|
+
name="include_metadata",
|
49
|
+
display_name="Include Metadata",
|
50
|
+
value=True,
|
51
|
+
info="Include video metadata like description and statistics.",
|
52
|
+
advanced=True,
|
53
|
+
),
|
54
|
+
]
|
55
|
+
|
56
|
+
outputs = [
|
57
|
+
Output(name="results", display_name="Search Results", method="search_videos"),
|
58
|
+
]
|
59
|
+
|
60
|
+
@contextmanager
|
61
|
+
def youtube_client(self):
|
62
|
+
"""Context manager for YouTube API client."""
|
63
|
+
client = build("youtube", "v3", developerKey=self.api_key)
|
64
|
+
try:
|
65
|
+
yield client
|
66
|
+
finally:
|
67
|
+
client.close()
|
68
|
+
|
69
|
+
def search_videos(self) -> DataFrame:
|
70
|
+
"""Searches YouTube videos and returns results as DataFrame."""
|
71
|
+
try:
|
72
|
+
with self.youtube_client() as youtube:
|
73
|
+
search_response = (
|
74
|
+
youtube.search()
|
75
|
+
.list(
|
76
|
+
q=self.query,
|
77
|
+
part="id,snippet",
|
78
|
+
maxResults=self.max_results,
|
79
|
+
order=self.order,
|
80
|
+
type="video",
|
81
|
+
)
|
82
|
+
.execute()
|
83
|
+
)
|
84
|
+
|
85
|
+
results = []
|
86
|
+
for search_result in search_response.get("items", []):
|
87
|
+
video_id = search_result["id"]["videoId"]
|
88
|
+
snippet = search_result["snippet"]
|
89
|
+
|
90
|
+
result = {
|
91
|
+
"video_id": video_id,
|
92
|
+
"title": snippet["title"],
|
93
|
+
"description": snippet["description"],
|
94
|
+
"published_at": snippet["publishedAt"],
|
95
|
+
"channel_title": snippet["channelTitle"],
|
96
|
+
"thumbnail_url": snippet["thumbnails"]["default"]["url"],
|
97
|
+
}
|
98
|
+
|
99
|
+
if self.include_metadata:
|
100
|
+
# Get video details for additional metadata
|
101
|
+
video_response = youtube.videos().list(part="statistics,contentDetails", id=video_id).execute()
|
102
|
+
|
103
|
+
if video_response.get("items"):
|
104
|
+
video_details = video_response["items"][0]
|
105
|
+
result.update(
|
106
|
+
{
|
107
|
+
"view_count": int(video_details["statistics"]["viewCount"]),
|
108
|
+
"like_count": int(video_details["statistics"].get("likeCount", 0)),
|
109
|
+
"comment_count": int(video_details["statistics"].get("commentCount", 0)),
|
110
|
+
"duration": video_details["contentDetails"]["duration"],
|
111
|
+
}
|
112
|
+
)
|
113
|
+
|
114
|
+
results.append(result)
|
115
|
+
|
116
|
+
return DataFrame(pd.DataFrame(results))
|
117
|
+
|
118
|
+
except HttpError as e:
|
119
|
+
error_message = f"YouTube API error: {e!s}"
|
120
|
+
return DataFrame(pd.DataFrame({"error": [error_message]}))
|
@@ -0,0 +1,285 @@
|
|
1
|
+
from contextlib import contextmanager
|
2
|
+
|
3
|
+
import pandas as pd
|
4
|
+
from googleapiclient.discovery import build
|
5
|
+
from googleapiclient.errors import HttpError
|
6
|
+
|
7
|
+
from lfx.custom.custom_component.component import Component
|
8
|
+
from lfx.inputs.inputs import BoolInput, DropdownInput, IntInput, SecretStrInput
|
9
|
+
from lfx.log.logger import logger
|
10
|
+
from lfx.schema.dataframe import DataFrame
|
11
|
+
from lfx.template.field.base import Output
|
12
|
+
|
13
|
+
HTTP_FORBIDDEN = 403
|
14
|
+
HTTP_NOT_FOUND = 404
|
15
|
+
MAX_API_RESULTS = 50
|
16
|
+
|
17
|
+
|
18
|
+
class YouTubeTrendingComponent(Component):
|
19
|
+
"""A component that retrieves trending videos from YouTube."""
|
20
|
+
|
21
|
+
display_name: str = "YouTube Trending"
|
22
|
+
description: str = "Retrieves trending videos from YouTube with filtering options."
|
23
|
+
icon: str = "YouTube"
|
24
|
+
|
25
|
+
# Dictionary of country codes and names
|
26
|
+
COUNTRY_CODES = {
|
27
|
+
"Global": "US", # Default to US for global
|
28
|
+
"United States": "US",
|
29
|
+
"Brazil": "BR",
|
30
|
+
"United Kingdom": "GB",
|
31
|
+
"India": "IN",
|
32
|
+
"Japan": "JP",
|
33
|
+
"South Korea": "KR",
|
34
|
+
"Germany": "DE",
|
35
|
+
"France": "FR",
|
36
|
+
"Canada": "CA",
|
37
|
+
"Australia": "AU",
|
38
|
+
"Spain": "ES",
|
39
|
+
"Italy": "IT",
|
40
|
+
"Mexico": "MX",
|
41
|
+
"Russia": "RU",
|
42
|
+
"Netherlands": "NL",
|
43
|
+
"Poland": "PL",
|
44
|
+
"Argentina": "AR",
|
45
|
+
}
|
46
|
+
|
47
|
+
# Dictionary of video categories
|
48
|
+
VIDEO_CATEGORIES = {
|
49
|
+
"All": "0",
|
50
|
+
"Film & Animation": "1",
|
51
|
+
"Autos & Vehicles": "2",
|
52
|
+
"Music": "10",
|
53
|
+
"Pets & Animals": "15",
|
54
|
+
"Sports": "17",
|
55
|
+
"Travel & Events": "19",
|
56
|
+
"Gaming": "20",
|
57
|
+
"People & Blogs": "22",
|
58
|
+
"Comedy": "23",
|
59
|
+
"Entertainment": "24",
|
60
|
+
"News & Politics": "25",
|
61
|
+
"Education": "27",
|
62
|
+
"Science & Technology": "28",
|
63
|
+
"Nonprofits & Activism": "29",
|
64
|
+
}
|
65
|
+
|
66
|
+
inputs = [
|
67
|
+
SecretStrInput(
|
68
|
+
name="api_key",
|
69
|
+
display_name="YouTube API Key",
|
70
|
+
info="Your YouTube Data API key.",
|
71
|
+
required=True,
|
72
|
+
),
|
73
|
+
DropdownInput(
|
74
|
+
name="region",
|
75
|
+
display_name="Region",
|
76
|
+
options=list(COUNTRY_CODES.keys()),
|
77
|
+
value="Global",
|
78
|
+
info="The region to get trending videos from.",
|
79
|
+
),
|
80
|
+
DropdownInput(
|
81
|
+
name="category",
|
82
|
+
display_name="Category",
|
83
|
+
options=list(VIDEO_CATEGORIES.keys()),
|
84
|
+
value="All",
|
85
|
+
info="The category of videos to retrieve.",
|
86
|
+
),
|
87
|
+
IntInput(
|
88
|
+
name="max_results",
|
89
|
+
display_name="Max Results",
|
90
|
+
value=10,
|
91
|
+
info="Maximum number of trending videos to return (1-50).",
|
92
|
+
),
|
93
|
+
BoolInput(
|
94
|
+
name="include_statistics",
|
95
|
+
display_name="Include Statistics",
|
96
|
+
value=True,
|
97
|
+
info="Include video statistics (views, likes, comments).",
|
98
|
+
),
|
99
|
+
BoolInput(
|
100
|
+
name="include_content_details",
|
101
|
+
display_name="Include Content Details",
|
102
|
+
value=True,
|
103
|
+
info="Include video duration and quality info.",
|
104
|
+
advanced=True,
|
105
|
+
),
|
106
|
+
BoolInput(
|
107
|
+
name="include_thumbnails",
|
108
|
+
display_name="Include Thumbnails",
|
109
|
+
value=True,
|
110
|
+
info="Include video thumbnail URLs.",
|
111
|
+
advanced=True,
|
112
|
+
),
|
113
|
+
]
|
114
|
+
|
115
|
+
outputs = [
|
116
|
+
Output(name="trending_videos", display_name="Trending Videos", method="get_trending_videos"),
|
117
|
+
]
|
118
|
+
|
119
|
+
max_results: int
|
120
|
+
|
121
|
+
def _format_duration(self, duration: str) -> str:
|
122
|
+
"""Formats ISO 8601 duration to readable format."""
|
123
|
+
import re
|
124
|
+
|
125
|
+
# Remove 'PT' from the start of duration
|
126
|
+
duration = duration[2:]
|
127
|
+
|
128
|
+
hours = 0
|
129
|
+
minutes = 0
|
130
|
+
seconds = 0
|
131
|
+
|
132
|
+
# Extract hours, minutes and seconds
|
133
|
+
time_dict = {}
|
134
|
+
for time_unit in ["H", "M", "S"]:
|
135
|
+
match = re.search(r"(\d+)" + time_unit, duration)
|
136
|
+
if match:
|
137
|
+
time_dict[time_unit] = int(match.group(1))
|
138
|
+
|
139
|
+
if "H" in time_dict:
|
140
|
+
hours = time_dict["H"]
|
141
|
+
if "M" in time_dict:
|
142
|
+
minutes = time_dict["M"]
|
143
|
+
if "S" in time_dict:
|
144
|
+
seconds = time_dict["S"]
|
145
|
+
|
146
|
+
# Format the time string
|
147
|
+
if hours > 0:
|
148
|
+
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
|
149
|
+
return f"{minutes:02d}:{seconds:02d}"
|
150
|
+
|
151
|
+
@contextmanager
|
152
|
+
def youtube_client(self):
|
153
|
+
"""Context manager for YouTube API client."""
|
154
|
+
client = build("youtube", "v3", developerKey=self.api_key)
|
155
|
+
try:
|
156
|
+
yield client
|
157
|
+
finally:
|
158
|
+
client.close()
|
159
|
+
|
160
|
+
def get_trending_videos(self) -> DataFrame:
|
161
|
+
"""Retrieves trending videos from YouTube and returns as DataFrame."""
|
162
|
+
try:
|
163
|
+
# Validate max_results
|
164
|
+
if not 1 <= self.max_results <= MAX_API_RESULTS:
|
165
|
+
self.max_results = min(max(1, self.max_results), MAX_API_RESULTS)
|
166
|
+
|
167
|
+
# Use context manager for YouTube API client
|
168
|
+
with self.youtube_client() as youtube:
|
169
|
+
# Get country code
|
170
|
+
region_code = self.COUNTRY_CODES[self.region]
|
171
|
+
|
172
|
+
# Prepare API request parts
|
173
|
+
parts = ["snippet"]
|
174
|
+
if self.include_statistics:
|
175
|
+
parts.append("statistics")
|
176
|
+
if self.include_content_details:
|
177
|
+
parts.append("contentDetails")
|
178
|
+
|
179
|
+
# Prepare API request parameters
|
180
|
+
request_params = {
|
181
|
+
"part": ",".join(parts),
|
182
|
+
"chart": "mostPopular",
|
183
|
+
"regionCode": region_code,
|
184
|
+
"maxResults": self.max_results,
|
185
|
+
}
|
186
|
+
|
187
|
+
# Add category filter if not "All"
|
188
|
+
if self.category != "All":
|
189
|
+
request_params["videoCategoryId"] = self.VIDEO_CATEGORIES[self.category]
|
190
|
+
|
191
|
+
# Get trending videos
|
192
|
+
request = youtube.videos().list(**request_params)
|
193
|
+
response = request.execute()
|
194
|
+
|
195
|
+
videos_data = []
|
196
|
+
for item in response.get("items", []):
|
197
|
+
video_data = {
|
198
|
+
"video_id": item["id"],
|
199
|
+
"title": item["snippet"]["title"],
|
200
|
+
"description": item["snippet"]["description"],
|
201
|
+
"channel_id": item["snippet"]["channelId"],
|
202
|
+
"channel_title": item["snippet"]["channelTitle"],
|
203
|
+
"published_at": item["snippet"]["publishedAt"],
|
204
|
+
"url": f"https://www.youtube.com/watch?v={item['id']}",
|
205
|
+
"region": self.region,
|
206
|
+
"category": self.category,
|
207
|
+
}
|
208
|
+
|
209
|
+
# Add thumbnails if requested
|
210
|
+
if self.include_thumbnails:
|
211
|
+
for size, thumb in item["snippet"]["thumbnails"].items():
|
212
|
+
video_data[f"thumbnail_{size}_url"] = thumb["url"]
|
213
|
+
video_data[f"thumbnail_{size}_width"] = thumb.get("width", 0)
|
214
|
+
video_data[f"thumbnail_{size}_height"] = thumb.get("height", 0)
|
215
|
+
|
216
|
+
# Add statistics if requested
|
217
|
+
if self.include_statistics and "statistics" in item:
|
218
|
+
video_data.update(
|
219
|
+
{
|
220
|
+
"view_count": int(item["statistics"].get("viewCount", 0)),
|
221
|
+
"like_count": int(item["statistics"].get("likeCount", 0)),
|
222
|
+
"comment_count": int(item["statistics"].get("commentCount", 0)),
|
223
|
+
}
|
224
|
+
)
|
225
|
+
|
226
|
+
# Add content details if requested
|
227
|
+
if self.include_content_details and "contentDetails" in item:
|
228
|
+
content_details = item["contentDetails"]
|
229
|
+
video_data.update(
|
230
|
+
{
|
231
|
+
"duration": self._format_duration(content_details["duration"]),
|
232
|
+
"definition": content_details.get("definition", "hd").upper(),
|
233
|
+
"has_captions": content_details.get("caption", "false") == "true",
|
234
|
+
"licensed_content": content_details.get("licensedContent", False),
|
235
|
+
"projection": content_details.get("projection", "rectangular"),
|
236
|
+
}
|
237
|
+
)
|
238
|
+
|
239
|
+
videos_data.append(video_data)
|
240
|
+
|
241
|
+
# Convert to DataFrame
|
242
|
+
videos_df = pd.DataFrame(videos_data)
|
243
|
+
|
244
|
+
# Organize columns
|
245
|
+
column_order = [
|
246
|
+
"video_id",
|
247
|
+
"title",
|
248
|
+
"channel_id",
|
249
|
+
"channel_title",
|
250
|
+
"category",
|
251
|
+
"region",
|
252
|
+
"published_at",
|
253
|
+
"url",
|
254
|
+
"description",
|
255
|
+
]
|
256
|
+
|
257
|
+
if self.include_statistics:
|
258
|
+
column_order.extend(["view_count", "like_count", "comment_count"])
|
259
|
+
|
260
|
+
if self.include_content_details:
|
261
|
+
column_order.extend(["duration", "definition", "has_captions", "licensed_content", "projection"])
|
262
|
+
|
263
|
+
# Add thumbnail columns at the end if included
|
264
|
+
if self.include_thumbnails:
|
265
|
+
thumbnail_cols = [col for col in videos_df.columns if col.startswith("thumbnail_")]
|
266
|
+
column_order.extend(sorted(thumbnail_cols))
|
267
|
+
|
268
|
+
# Reorder columns, including any that might not be in column_order
|
269
|
+
remaining_cols = [col for col in videos_df.columns if col not in column_order]
|
270
|
+
videos_df = videos_df[column_order + remaining_cols]
|
271
|
+
|
272
|
+
return DataFrame(videos_df)
|
273
|
+
|
274
|
+
except HttpError as e:
|
275
|
+
error_message = f"YouTube API error: {e}"
|
276
|
+
if e.resp.status == HTTP_FORBIDDEN:
|
277
|
+
error_message = "API quota exceeded or access forbidden."
|
278
|
+
elif e.resp.status == HTTP_NOT_FOUND:
|
279
|
+
error_message = "Resource not found."
|
280
|
+
|
281
|
+
return DataFrame(pd.DataFrame({"error": [error_message]}))
|
282
|
+
|
283
|
+
except Exception as e: # noqa: BLE001
|
284
|
+
logger.exception("An unexpected error occurred:")
|
285
|
+
return DataFrame(pd.DataFrame({"error": [str(e)]}))
|