From a082afbcda84fdfc9b6ab70b96da6041a60a61cd Mon Sep 17 00:00:00 2001 From: Eric Hare Date: Tue, 16 Jun 2026 11:28:39 -0700 Subject: [PATCH] feat(bundles): consolidate cleanlab, twelvelabs, jigsawstack into lfx-bundles First directory-level batch of the core-tail consolidation (validates the consolidate_bundles.py path end-to-end after the spider/toolguard file-level extractions). All three are flat, lfx-only, single-SDK providers: - cleanlab -> lfx_bundles/cleanlab (cleanlab-tlm) 3 components - twelvelabs -> lfx_bundles/twelvelabs (twelvelabs) 7 components - jigsawstack -> lfx_bundles/jigsawstack (jigsawstack) 11 components Via scripts/migrate/consolidate_bundles.py --apply: moves each dir into the metapackage + leaves a fail-soft shim, merges per-provider extras and regen `all`, appends the 4-entry migration block per class (84 entries, 0 ambiguous). Component index regenerated (45->42 categories, 263->242 core components); uv.lock re-resolved; twelvelabs test repointed to lfx_bundles.twelvelabs (passes). Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/migrate/consolidate_bundles.py | 4 + .../twelvelabs/test_twelvelabs_components.py | 4 +- src/bundles/lfx-bundles/pyproject.toml | 6 + .../src/lfx_bundles/cleanlab/__init__.py | 40 + .../cleanlab/cleanlab_evaluator.py | 1 - .../cleanlab/cleanlab_rag_evaluator.py | 1 - .../cleanlab/cleanlab_remediator.py | 0 .../src/lfx_bundles/jigsawstack/__init__.py | 23 + .../src/lfx_bundles}/jigsawstack/ai_scrape.py | 0 .../lfx_bundles}/jigsawstack/ai_web_search.py | 0 .../src/lfx_bundles}/jigsawstack/file_read.py | 0 .../lfx_bundles}/jigsawstack/file_upload.py | 0 .../jigsawstack/image_generation.py | 0 .../src/lfx_bundles}/jigsawstack/nsfw.py | 0 .../jigsawstack/object_detection.py | 0 .../src/lfx_bundles}/jigsawstack/sentiment.py | 0 .../lfx_bundles}/jigsawstack/text_to_sql.py | 0 .../jigsawstack/text_translate.py | 0 .../src/lfx_bundles}/jigsawstack/vocr.py | 0 .../src/lfx_bundles/twelvelabs/__init__.py | 52 + .../twelvelabs/convert_astra_results.py | 0 .../lfx_bundles}/twelvelabs/pegasus_index.py | 5 +- .../lfx_bundles}/twelvelabs/split_video.py | 0 .../twelvelabs/text_embeddings.py | 3 +- .../twelvelabs/twelvelabs_pegasus.py | 5 +- .../twelvelabs/video_embeddings.py | 3 +- .../src/lfx_bundles}/twelvelabs/video_file.py | 0 src/lfx/src/lfx/_assets/component_index.json | 4239 +---------------- .../src/lfx/components/cleanlab/__init__.py | 56 +- .../lfx/components/jigsawstack/__init__.py | 43 +- .../src/lfx/components/twelvelabs/__init__.py | 68 +- .../extension/migration/migration_table.json | 420 ++ uv.lock | 20 +- 33 files changed, 634 insertions(+), 4359 deletions(-) create mode 100644 src/bundles/lfx-bundles/src/lfx_bundles/cleanlab/__init__.py rename src/{lfx/src/lfx/components => bundles/lfx-bundles/src/lfx_bundles}/cleanlab/cleanlab_evaluator.py (99%) rename src/{lfx/src/lfx/components => bundles/lfx-bundles/src/lfx_bundles}/cleanlab/cleanlab_rag_evaluator.py (99%) rename src/{lfx/src/lfx/components => bundles/lfx-bundles/src/lfx_bundles}/cleanlab/cleanlab_remediator.py (100%) create mode 100644 src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/__init__.py rename src/{lfx/src/lfx/components => bundles/lfx-bundles/src/lfx_bundles}/jigsawstack/ai_scrape.py (100%) rename src/{lfx/src/lfx/components => bundles/lfx-bundles/src/lfx_bundles}/jigsawstack/ai_web_search.py (100%) rename src/{lfx/src/lfx/components => bundles/lfx-bundles/src/lfx_bundles}/jigsawstack/file_read.py (100%) rename src/{lfx/src/lfx/components => bundles/lfx-bundles/src/lfx_bundles}/jigsawstack/file_upload.py (100%) rename src/{lfx/src/lfx/components => bundles/lfx-bundles/src/lfx_bundles}/jigsawstack/image_generation.py (100%) rename src/{lfx/src/lfx/components => bundles/lfx-bundles/src/lfx_bundles}/jigsawstack/nsfw.py (100%) rename src/{lfx/src/lfx/components => bundles/lfx-bundles/src/lfx_bundles}/jigsawstack/object_detection.py (100%) rename src/{lfx/src/lfx/components => bundles/lfx-bundles/src/lfx_bundles}/jigsawstack/sentiment.py (100%) rename src/{lfx/src/lfx/components => bundles/lfx-bundles/src/lfx_bundles}/jigsawstack/text_to_sql.py (100%) rename src/{lfx/src/lfx/components => bundles/lfx-bundles/src/lfx_bundles}/jigsawstack/text_translate.py (100%) rename src/{lfx/src/lfx/components => bundles/lfx-bundles/src/lfx_bundles}/jigsawstack/vocr.py (100%) create mode 100644 src/bundles/lfx-bundles/src/lfx_bundles/twelvelabs/__init__.py rename src/{lfx/src/lfx/components => bundles/lfx-bundles/src/lfx_bundles}/twelvelabs/convert_astra_results.py (100%) rename src/{lfx/src/lfx/components => bundles/lfx-bundles/src/lfx_bundles}/twelvelabs/pegasus_index.py (99%) rename src/{lfx/src/lfx/components => bundles/lfx-bundles/src/lfx_bundles}/twelvelabs/split_video.py (100%) rename src/{lfx/src/lfx/components => bundles/lfx-bundles/src/lfx_bundles}/twelvelabs/text_embeddings.py (99%) rename src/{lfx/src/lfx/components => bundles/lfx-bundles/src/lfx_bundles}/twelvelabs/twelvelabs_pegasus.py (99%) rename src/{lfx/src/lfx/components => bundles/lfx-bundles/src/lfx_bundles}/twelvelabs/video_embeddings.py (99%) rename src/{lfx/src/lfx/components => bundles/lfx-bundles/src/lfx_bundles}/twelvelabs/video_file.py (100%) diff --git a/scripts/migrate/consolidate_bundles.py b/scripts/migrate/consolidate_bundles.py index a906193a2b..eedaf6e12c 100755 --- a/scripts/migrate/consolidate_bundles.py +++ b/scripts/migrate/consolidate_bundles.py @@ -114,6 +114,10 @@ PROVIDER_DEPS: dict[str, list[str]] = { "openrouter": ["langchain-openai>=1.1.6"], "vllm": ["langchain-openai>=1.1.6", "openai>=1.68.2,<3.0.0"], "xai": ["langchain-openai>=1.1.6", "openai>=1.68.2,<3.0.0", "requests>=2.32.0"], + # --- tranche 4: remaining single-SDK providers (post core-tail audit) --- + "cleanlab": ["cleanlab-tlm>=1.1.2,<2.0.0"], + "twelvelabs": ["twelvelabs>=0.4.7,<1.0.0"], + "jigsawstack": ["jigsawstack==0.2.7"], } _OPTIONAL_DEPS_HEADER = ( diff --git a/src/backend/tests/unit/components/bundles/twelvelabs/test_twelvelabs_components.py b/src/backend/tests/unit/components/bundles/twelvelabs/test_twelvelabs_components.py index 986e5b2e66..eaf7069194 100644 --- a/src/backend/tests/unit/components/bundles/twelvelabs/test_twelvelabs_components.py +++ b/src/backend/tests/unit/components/bundles/twelvelabs/test_twelvelabs_components.py @@ -4,8 +4,8 @@ import os from unittest.mock import patch import pytest -from lfx.components.twelvelabs.split_video import SplitVideoComponent -from lfx.components.twelvelabs.video_file import VideoFileComponent +from lfx_bundles.twelvelabs.split_video import SplitVideoComponent +from lfx_bundles.twelvelabs.video_file import VideoFileComponent @pytest.mark.unit diff --git a/src/bundles/lfx-bundles/pyproject.toml b/src/bundles/lfx-bundles/pyproject.toml index 2f2b6db813..4840bafd7e 100644 --- a/src/bundles/lfx-bundles/pyproject.toml +++ b/src/bundles/lfx-bundles/pyproject.toml @@ -31,6 +31,7 @@ apify = ["apify-client>=1.8.1", "langchain-community>=0.4.1,<1.0.0"] assemblyai = ["assemblyai>=0.33.0,<1.0.0"] azure = ["langchain-openai>=1.1.6"] chroma = ["chromadb>=1.0.0,<2.0.0", "langchain-chroma~=0.2.6", "langchain-community>=0.4.1,<1.0.0"] +cleanlab = ["cleanlab-tlm>=1.1.2,<2.0.0"] clickhouse = ["clickhouse-connect==0.7.19", "langchain-community>=0.4.1,<1.0.0"] cometapi = ["langchain-openai>=1.1.6", "requests>=2.32.0"] confluence = ["atlassian-python-api==3.41.16", "langchain-community>=0.4.1,<1.0.0"] @@ -42,6 +43,7 @@ git = ["GitPython>=3.1.50", "langchain-community>=0.4.1,<1.0.0"] glean = [] groq = ["langchain-groq~=1.1.1"] icosacomputing = ["requests>=2.32.0"] +jigsawstack = ["jigsawstack==0.2.7"] litellm = ["langchain-openai>=1.1.6", "openai>=1.68.2,<3.0.0"] lmstudio = ["langchain-openai>=1.1.6", "openai>=1.68.2,<3.0.0", "langchain-nvidia-ai-endpoints~=1.0.0"] mem0 = ["mem0ai>=2.0.2,<3.0.0"] @@ -63,6 +65,7 @@ spider = ["spider-client>=0.0.27,<1.0.0"] supabase = ["supabase>=2.6.0,<3.0.0", "langchain-community>=0.4.1,<1.0.0"] tavily = [] toolguard = ["toolguard>=0.2.16,<1.0.0"] +twelvelabs = ["twelvelabs>=0.4.7,<1.0.0"] unstructured = ["langchain-unstructured~=1.0.0"] upstash = ["upstash-vector==0.6.0", "langchain-community>=0.4.1,<1.0.0"] vllm = ["langchain-openai>=1.1.6", "openai>=1.68.2,<3.0.0"] @@ -79,6 +82,7 @@ all = [ "lfx-bundles[assemblyai]", "lfx-bundles[azure]", "lfx-bundles[chroma]", + "lfx-bundles[cleanlab]", "lfx-bundles[clickhouse]", "lfx-bundles[cometapi]", "lfx-bundles[confluence]", @@ -90,6 +94,7 @@ all = [ "lfx-bundles[glean]", "lfx-bundles[groq]", "lfx-bundles[icosacomputing]", + "lfx-bundles[jigsawstack]", "lfx-bundles[litellm]", "lfx-bundles[lmstudio]", "lfx-bundles[mem0]", @@ -111,6 +116,7 @@ all = [ "lfx-bundles[supabase]", "lfx-bundles[tavily]", "lfx-bundles[toolguard]", + "lfx-bundles[twelvelabs]", "lfx-bundles[unstructured]", "lfx-bundles[upstash]", "lfx-bundles[vllm]", diff --git a/src/bundles/lfx-bundles/src/lfx_bundles/cleanlab/__init__.py b/src/bundles/lfx-bundles/src/lfx_bundles/cleanlab/__init__.py new file mode 100644 index 0000000000..4473e5d2f7 --- /dev/null +++ b/src/bundles/lfx-bundles/src/lfx_bundles/cleanlab/__init__.py @@ -0,0 +1,40 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +from lfx.components._importing import import_mod + +if TYPE_CHECKING: + from .cleanlab_evaluator import CleanlabEvaluator + from .cleanlab_rag_evaluator import CleanlabRAGEvaluator + from .cleanlab_remediator import CleanlabRemediator + +_dynamic_imports = { + "CleanlabEvaluator": "cleanlab_evaluator", + "CleanlabRAGEvaluator": "cleanlab_rag_evaluator", + "CleanlabRemediator": "cleanlab_remediator", +} + +__all__ = [ + "CleanlabEvaluator", + "CleanlabRAGEvaluator", + "CleanlabRemediator", +] + + +def __getattr__(attr_name: str) -> Any: + """Lazily import cleanlab components on attribute access.""" + if attr_name not in _dynamic_imports: + msg = f"module '{__name__}' has no attribute '{attr_name}'" + raise AttributeError(msg) + try: + result = import_mod(attr_name, _dynamic_imports[attr_name], __spec__.parent) + except (ModuleNotFoundError, ImportError, AttributeError) as e: + msg = f"Could not import '{attr_name}' from '{__name__}': {e}" + raise AttributeError(msg) from e + globals()[attr_name] = result + return result + + +def __dir__() -> list[str]: + return list(__all__) diff --git a/src/lfx/src/lfx/components/cleanlab/cleanlab_evaluator.py b/src/bundles/lfx-bundles/src/lfx_bundles/cleanlab/cleanlab_evaluator.py similarity index 99% rename from src/lfx/src/lfx/components/cleanlab/cleanlab_evaluator.py rename to src/bundles/lfx-bundles/src/lfx_bundles/cleanlab/cleanlab_evaluator.py index 87e80f8e40..eed33752f7 100644 --- a/src/lfx/src/lfx/components/cleanlab/cleanlab_evaluator.py +++ b/src/bundles/lfx-bundles/src/lfx_bundles/cleanlab/cleanlab_evaluator.py @@ -1,5 +1,4 @@ from cleanlab_tlm import TLM - from lfx.custom import Component from lfx.io import ( DropdownInput, diff --git a/src/lfx/src/lfx/components/cleanlab/cleanlab_rag_evaluator.py b/src/bundles/lfx-bundles/src/lfx_bundles/cleanlab/cleanlab_rag_evaluator.py similarity index 99% rename from src/lfx/src/lfx/components/cleanlab/cleanlab_rag_evaluator.py rename to src/bundles/lfx-bundles/src/lfx_bundles/cleanlab/cleanlab_rag_evaluator.py index f286ee448f..f7b6f3b696 100644 --- a/src/lfx/src/lfx/components/cleanlab/cleanlab_rag_evaluator.py +++ b/src/bundles/lfx-bundles/src/lfx_bundles/cleanlab/cleanlab_rag_evaluator.py @@ -1,5 +1,4 @@ from cleanlab_tlm import TrustworthyRAG, get_default_evals - from lfx.custom import Component from lfx.io import ( BoolInput, diff --git a/src/lfx/src/lfx/components/cleanlab/cleanlab_remediator.py b/src/bundles/lfx-bundles/src/lfx_bundles/cleanlab/cleanlab_remediator.py similarity index 100% rename from src/lfx/src/lfx/components/cleanlab/cleanlab_remediator.py rename to src/bundles/lfx-bundles/src/lfx_bundles/cleanlab/cleanlab_remediator.py diff --git a/src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/__init__.py b/src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/__init__.py new file mode 100644 index 0000000000..c32d56ee71 --- /dev/null +++ b/src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/__init__.py @@ -0,0 +1,23 @@ +from .ai_scrape import JigsawStackAIScraperComponent +from .ai_web_search import JigsawStackAIWebSearchComponent +from .file_read import JigsawStackFileReadComponent +from .file_upload import JigsawStackFileUploadComponent +from .image_generation import JigsawStackImageGenerationComponent +from .nsfw import JigsawStackNSFWComponent +from .object_detection import JigsawStackObjectDetectionComponent +from .sentiment import JigsawStackSentimentComponent +from .text_to_sql import JigsawStackTextToSQLComponent +from .vocr import JigsawStackVOCRComponent + +__all__ = [ + "JigsawStackAIScraperComponent", + "JigsawStackAIWebSearchComponent", + "JigsawStackFileReadComponent", + "JigsawStackFileUploadComponent", + "JigsawStackImageGenerationComponent", + "JigsawStackNSFWComponent", + "JigsawStackObjectDetectionComponent", + "JigsawStackSentimentComponent", + "JigsawStackTextToSQLComponent", + "JigsawStackVOCRComponent", +] diff --git a/src/lfx/src/lfx/components/jigsawstack/ai_scrape.py b/src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/ai_scrape.py similarity index 100% rename from src/lfx/src/lfx/components/jigsawstack/ai_scrape.py rename to src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/ai_scrape.py diff --git a/src/lfx/src/lfx/components/jigsawstack/ai_web_search.py b/src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/ai_web_search.py similarity index 100% rename from src/lfx/src/lfx/components/jigsawstack/ai_web_search.py rename to src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/ai_web_search.py diff --git a/src/lfx/src/lfx/components/jigsawstack/file_read.py b/src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/file_read.py similarity index 100% rename from src/lfx/src/lfx/components/jigsawstack/file_read.py rename to src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/file_read.py diff --git a/src/lfx/src/lfx/components/jigsawstack/file_upload.py b/src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/file_upload.py similarity index 100% rename from src/lfx/src/lfx/components/jigsawstack/file_upload.py rename to src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/file_upload.py diff --git a/src/lfx/src/lfx/components/jigsawstack/image_generation.py b/src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/image_generation.py similarity index 100% rename from src/lfx/src/lfx/components/jigsawstack/image_generation.py rename to src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/image_generation.py diff --git a/src/lfx/src/lfx/components/jigsawstack/nsfw.py b/src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/nsfw.py similarity index 100% rename from src/lfx/src/lfx/components/jigsawstack/nsfw.py rename to src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/nsfw.py diff --git a/src/lfx/src/lfx/components/jigsawstack/object_detection.py b/src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/object_detection.py similarity index 100% rename from src/lfx/src/lfx/components/jigsawstack/object_detection.py rename to src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/object_detection.py diff --git a/src/lfx/src/lfx/components/jigsawstack/sentiment.py b/src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/sentiment.py similarity index 100% rename from src/lfx/src/lfx/components/jigsawstack/sentiment.py rename to src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/sentiment.py diff --git a/src/lfx/src/lfx/components/jigsawstack/text_to_sql.py b/src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/text_to_sql.py similarity index 100% rename from src/lfx/src/lfx/components/jigsawstack/text_to_sql.py rename to src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/text_to_sql.py diff --git a/src/lfx/src/lfx/components/jigsawstack/text_translate.py b/src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/text_translate.py similarity index 100% rename from src/lfx/src/lfx/components/jigsawstack/text_translate.py rename to src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/text_translate.py diff --git a/src/lfx/src/lfx/components/jigsawstack/vocr.py b/src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/vocr.py similarity index 100% rename from src/lfx/src/lfx/components/jigsawstack/vocr.py rename to src/bundles/lfx-bundles/src/lfx_bundles/jigsawstack/vocr.py diff --git a/src/bundles/lfx-bundles/src/lfx_bundles/twelvelabs/__init__.py b/src/bundles/lfx-bundles/src/lfx_bundles/twelvelabs/__init__.py new file mode 100644 index 0000000000..526274a6b4 --- /dev/null +++ b/src/bundles/lfx-bundles/src/lfx_bundles/twelvelabs/__init__.py @@ -0,0 +1,52 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +from lfx.components._importing import import_mod + +if TYPE_CHECKING: + from .convert_astra_results import ConvertAstraToTwelveLabs + from .pegasus_index import PegasusIndexVideo + from .split_video import SplitVideoComponent + from .text_embeddings import TwelveLabsTextEmbeddingsComponent + from .twelvelabs_pegasus import TwelveLabsPegasus + from .video_embeddings import TwelveLabsVideoEmbeddingsComponent + from .video_file import VideoFileComponent + +_dynamic_imports = { + "ConvertAstraToTwelveLabs": "convert_astra_results", + "PegasusIndexVideo": "pegasus_index", + "SplitVideoComponent": "split_video", + "TwelveLabsPegasus": "twelvelabs_pegasus", + "TwelveLabsTextEmbeddingsComponent": "text_embeddings", + "TwelveLabsVideoEmbeddingsComponent": "video_embeddings", + "VideoFileComponent": "video_file", +} + +__all__ = [ + "ConvertAstraToTwelveLabs", + "PegasusIndexVideo", + "SplitVideoComponent", + "TwelveLabsPegasus", + "TwelveLabsTextEmbeddingsComponent", + "TwelveLabsVideoEmbeddingsComponent", + "VideoFileComponent", +] + + +def __getattr__(attr_name: str) -> Any: + """Lazily import twelvelabs components on attribute access.""" + if attr_name not in _dynamic_imports: + msg = f"module '{__name__}' has no attribute '{attr_name}'" + raise AttributeError(msg) + try: + result = import_mod(attr_name, _dynamic_imports[attr_name], __spec__.parent) + except (ModuleNotFoundError, ImportError, AttributeError) as e: + msg = f"Could not import '{attr_name}' from '{__name__}': {e}" + raise AttributeError(msg) from e + globals()[attr_name] = result + return result + + +def __dir__() -> list[str]: + return list(__all__) diff --git a/src/lfx/src/lfx/components/twelvelabs/convert_astra_results.py b/src/bundles/lfx-bundles/src/lfx_bundles/twelvelabs/convert_astra_results.py similarity index 100% rename from src/lfx/src/lfx/components/twelvelabs/convert_astra_results.py rename to src/bundles/lfx-bundles/src/lfx_bundles/twelvelabs/convert_astra_results.py diff --git a/src/lfx/src/lfx/components/twelvelabs/pegasus_index.py b/src/bundles/lfx-bundles/src/lfx_bundles/twelvelabs/pegasus_index.py similarity index 99% rename from src/lfx/src/lfx/components/twelvelabs/pegasus_index.py rename to src/bundles/lfx-bundles/src/lfx_bundles/twelvelabs/pegasus_index.py index e234d2c5fa..191fd727e8 100644 --- a/src/lfx/src/lfx/components/twelvelabs/pegasus_index.py +++ b/src/bundles/lfx-bundles/src/lfx_bundles/twelvelabs/pegasus_index.py @@ -3,13 +3,12 @@ from concurrent.futures import ThreadPoolExecutor from pathlib import Path from typing import Any -from tenacity import retry, stop_after_attempt, wait_exponential -from twelvelabs import TwelveLabs - from lfx.custom import Component from lfx.inputs import DataInput, DropdownInput, SecretStrInput, StrInput from lfx.io import Output from lfx.schema import Data +from tenacity import retry, stop_after_attempt, wait_exponential +from twelvelabs import TwelveLabs class TwelveLabsError(Exception): diff --git a/src/lfx/src/lfx/components/twelvelabs/split_video.py b/src/bundles/lfx-bundles/src/lfx_bundles/twelvelabs/split_video.py similarity index 100% rename from src/lfx/src/lfx/components/twelvelabs/split_video.py rename to src/bundles/lfx-bundles/src/lfx_bundles/twelvelabs/split_video.py diff --git a/src/lfx/src/lfx/components/twelvelabs/text_embeddings.py b/src/bundles/lfx-bundles/src/lfx_bundles/twelvelabs/text_embeddings.py similarity index 99% rename from src/lfx/src/lfx/components/twelvelabs/text_embeddings.py rename to src/bundles/lfx-bundles/src/lfx_bundles/twelvelabs/text_embeddings.py index 809bd70ce6..d1dbe630f2 100644 --- a/src/lfx/src/lfx/components/twelvelabs/text_embeddings.py +++ b/src/bundles/lfx-bundles/src/lfx_bundles/twelvelabs/text_embeddings.py @@ -1,8 +1,7 @@ -from twelvelabs import TwelveLabs - from lfx.base.embeddings.model import LCEmbeddingsModel from lfx.field_typing import Embeddings from lfx.io import DropdownInput, FloatInput, IntInput, SecretStrInput +from twelvelabs import TwelveLabs class TwelveLabsTextEmbeddings(Embeddings): diff --git a/src/lfx/src/lfx/components/twelvelabs/twelvelabs_pegasus.py b/src/bundles/lfx-bundles/src/lfx_bundles/twelvelabs/twelvelabs_pegasus.py similarity index 99% rename from src/lfx/src/lfx/components/twelvelabs/twelvelabs_pegasus.py rename to src/bundles/lfx-bundles/src/lfx_bundles/twelvelabs/twelvelabs_pegasus.py index 447e5659da..dc2b43c60b 100644 --- a/src/lfx/src/lfx/components/twelvelabs/twelvelabs_pegasus.py +++ b/src/bundles/lfx-bundles/src/lfx_bundles/twelvelabs/twelvelabs_pegasus.py @@ -4,14 +4,13 @@ import time from pathlib import Path from typing import Any -from tenacity import retry, stop_after_attempt, wait_exponential -from twelvelabs import TwelveLabs - from lfx.custom import Component from lfx.field_typing.range_spec import RangeSpec from lfx.inputs import DataInput, DropdownInput, MessageInput, MultilineInput, SecretStrInput, SliderInput from lfx.io import Output from lfx.schema.message import Message +from tenacity import retry, stop_after_attempt, wait_exponential +from twelvelabs import TwelveLabs class TaskError(Exception): diff --git a/src/lfx/src/lfx/components/twelvelabs/video_embeddings.py b/src/bundles/lfx-bundles/src/lfx_bundles/twelvelabs/video_embeddings.py similarity index 99% rename from src/lfx/src/lfx/components/twelvelabs/video_embeddings.py rename to src/bundles/lfx-bundles/src/lfx_bundles/twelvelabs/video_embeddings.py index 7e1b723d0b..ed3e4d6edc 100644 --- a/src/lfx/src/lfx/components/twelvelabs/video_embeddings.py +++ b/src/bundles/lfx-bundles/src/lfx_bundles/twelvelabs/video_embeddings.py @@ -2,11 +2,10 @@ import time from pathlib import Path from typing import Any, cast -from twelvelabs import TwelveLabs - from lfx.base.embeddings.model import LCEmbeddingsModel from lfx.field_typing import Embeddings from lfx.io import DropdownInput, IntInput, SecretStrInput +from twelvelabs import TwelveLabs class TwelveLabsVideoEmbeddings(Embeddings): diff --git a/src/lfx/src/lfx/components/twelvelabs/video_file.py b/src/bundles/lfx-bundles/src/lfx_bundles/twelvelabs/video_file.py similarity index 100% rename from src/lfx/src/lfx/components/twelvelabs/video_file.py rename to src/bundles/lfx-bundles/src/lfx_bundles/twelvelabs/video_file.py diff --git a/src/lfx/src/lfx/_assets/component_index.json b/src/lfx/src/lfx/_assets/component_index.json index f3ca40e1c6..604b99b023 100644 --- a/src/lfx/src/lfx/_assets/component_index.json +++ b/src/lfx/src/lfx/_assets/component_index.json @@ -5587,924 +5587,6 @@ } } ], - [ - "cleanlab", - { - "CleanlabEvaluator": { - "base_classes": [ - "float", - "Message", - "number" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Evaluates any LLM response using Cleanlab and outputs trust score and explanation.", - "display_name": "Cleanlab Evaluator", - "documentation": "", - "edited": false, - "field_order": [ - "system_prompt", - "prompt", - "response", - "api_key", - "model", - "quality_preset" - ], - "frozen": false, - "icon": "Cleanlab", - "legacy": false, - "metadata": { - "code_hash": "06963c804ffe", - "dependencies": { - "dependencies": [ - { - "name": "cleanlab_tlm", - "version": "1.1.39" - }, - { - "name": "lfx", - "version": null - } - ], - "total_dependencies": 2 - }, - "module": "lfx.components.cleanlab.cleanlab_evaluator.CleanlabEvaluator" - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Response", - "group_outputs": false, - "method": "pass_response", - "name": "response_passthrough", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "Trust Score", - "group_outputs": false, - "method": "get_score", - "name": "score", - "selected": "number", - "tool_mode": true, - "types": [ - "number", - "float" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "Explanation", - "group_outputs": false, - "method": "get_explanation", - "name": "explanation", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "api_key": { - "_input_type": "SecretStrInput", - "advanced": false, - "display_name": "Cleanlab API Key", - "dynamic": false, - "info": "Your Cleanlab API key.", - "input_types": [], - "load_from_db": true, - "name": "api_key", - "override_skip": false, - "password": true, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from cleanlab_tlm import TLM\n\nfrom lfx.custom import Component\nfrom lfx.io import (\n DropdownInput,\n MessageTextInput,\n Output,\n SecretStrInput,\n)\nfrom lfx.schema.message import Message\n\n\nclass CleanlabEvaluator(Component):\n \"\"\"A component that evaluates the trustworthiness of LLM responses using Cleanlab.\n\n This component takes a prompt and response pair, along with optional system instructions,\n and uses Cleanlab's evaluation algorithms to generate a trust score and explanation.\n\n Inputs:\n - system_prompt (MessageTextInput): Optional system-level instructions prepended to the user prompt.\n - prompt (MessageTextInput): The user's prompt or query sent to the LLM.\n - response (MessageTextInput): The response generated by the LLM to be evaluated. This should come from the\n LLM component, i.e. OpenAI, Gemini, etc.\n - api_key (SecretStrInput): Your Cleanlab API key.\n - model (DropdownInput): The model used by Cleanlab to evaluate the response (can differ from the\n generation model).\n - quality_preset (DropdownInput): Tradeoff setting for accuracy vs. speed and cost. Higher presets are\n slower but more accurate.\n\n Outputs:\n - response_passthrough (Message): The original response, passed through for downstream use.\n - score (number): A float between 0 and 1 indicating Cleanlab's trustworthiness score for the response.\n - explanation (Message): A textual explanation of why the response received its score.\n\n This component works well in conjunction with the CleanlabRemediator to create a complete trust evaluation\n and remediation pipeline.\n\n More details on the evaluation metrics can be found here: https://help.cleanlab.ai/tlm/tutorials/tlm/\n \"\"\"\n\n display_name = \"Cleanlab Evaluator\"\n description = \"Evaluates any LLM response using Cleanlab and outputs trust score and explanation.\"\n icon = \"Cleanlab\"\n name = \"CleanlabEvaluator\"\n\n inputs = [\n MessageTextInput(\n name=\"system_prompt\",\n display_name=\"System Message\",\n info=\"System-level instructions prepended to the user query.\",\n value=\"\",\n ),\n MessageTextInput(\n name=\"prompt\",\n display_name=\"Prompt\",\n info=\"The user's query to the model.\",\n required=True,\n ),\n MessageTextInput(\n name=\"response\",\n display_name=\"Response\",\n info=\"The response to the user's query.\",\n required=True,\n ),\n SecretStrInput(\n name=\"api_key\",\n display_name=\"Cleanlab API Key\",\n info=\"Your Cleanlab API key.\",\n required=True,\n ),\n DropdownInput(\n name=\"model\",\n display_name=\"Cleanlab Evaluation Model\",\n options=[\n \"gpt-4.1\",\n \"gpt-4.1-mini\",\n \"gpt-4.1-nano\",\n \"o4-mini\",\n \"o3\",\n \"gpt-4.5-preview\",\n \"gpt-4o-mini\",\n \"gpt-4o\",\n \"o3-mini\",\n \"o1\",\n \"o1-mini\",\n \"gpt-4\",\n \"gpt-3.5-turbo-16k\",\n \"claude-3.7-sonnet\",\n \"claude-3.5-sonnet-v2\",\n \"claude-3.5-sonnet\",\n \"claude-3.5-haiku\",\n \"claude-3-haiku\",\n \"nova-micro\",\n \"nova-lite\",\n \"nova-pro\",\n ],\n info=\"The model Cleanlab uses to evaluate the response. This does NOT need to be the same model that \"\n \"generated the response.\",\n value=\"gpt-4o-mini\",\n required=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"quality_preset\",\n display_name=\"Quality Preset\",\n options=[\"base\", \"low\", \"medium\", \"high\", \"best\"],\n value=\"medium\",\n info=\"This determines the accuracy, latency, and cost of the evaluation. Higher quality is generally \"\n \"slower but more accurate.\",\n required=True,\n advanced=True,\n ),\n ]\n\n outputs = [\n Output(\n display_name=\"Response\",\n name=\"response_passthrough\",\n method=\"pass_response\",\n types=[\"Message\"],\n ),\n Output(display_name=\"Trust Score\", name=\"score\", method=\"get_score\", types=[\"number\"]),\n Output(\n display_name=\"Explanation\",\n name=\"explanation\",\n method=\"get_explanation\",\n types=[\"Message\"],\n ),\n ]\n\n def _evaluate_once(self):\n if not hasattr(self, \"_cached_result\"):\n full_prompt = f\"{self.system_prompt}\\n\\n{self.prompt}\" if self.system_prompt else self.prompt\n tlm = TLM(\n api_key=self.api_key,\n options={\"log\": [\"explanation\"], \"model\": self.model},\n quality_preset=self.quality_preset,\n )\n self._cached_result = tlm.get_trustworthiness_score(full_prompt, self.response)\n return self._cached_result\n\n def get_score(self) -> float:\n result = self._evaluate_once()\n score = result.get(\"trustworthiness_score\", 0.0)\n self.status = f\"Trust score: {score:.2f}\"\n return score\n\n def get_explanation(self) -> Message:\n result = self._evaluate_once()\n explanation = result.get(\"log\", {}).get(\"explanation\", \"No explanation returned.\")\n return Message(text=explanation)\n\n def pass_response(self) -> Message:\n self.status = \"Passing through response.\"\n return Message(text=self.response)\n" - }, - "model": { - "_input_type": "DropdownInput", - "advanced": true, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Cleanlab Evaluation Model", - "dynamic": false, - "external_options": {}, - "info": "The model Cleanlab uses to evaluate the response. This does NOT need to be the same model that generated the response.", - "name": "model", - "options": [ - "gpt-4.1", - "gpt-4.1-mini", - "gpt-4.1-nano", - "o4-mini", - "o3", - "gpt-4.5-preview", - "gpt-4o-mini", - "gpt-4o", - "o3-mini", - "o1", - "o1-mini", - "gpt-4", - "gpt-3.5-turbo-16k", - "claude-3.7-sonnet", - "claude-3.5-sonnet-v2", - "claude-3.5-sonnet", - "claude-3.5-haiku", - "claude-3-haiku", - "nova-micro", - "nova-lite", - "nova-pro" - ], - "options_metadata": [], - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "toggle": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "str", - "value": "gpt-4o-mini" - }, - "prompt": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "Prompt", - "dynamic": false, - "info": "The user's query to the model.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "prompt", - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "quality_preset": { - "_input_type": "DropdownInput", - "advanced": true, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Quality Preset", - "dynamic": false, - "external_options": {}, - "info": "This determines the accuracy, latency, and cost of the evaluation. Higher quality is generally slower but more accurate.", - "name": "quality_preset", - "options": [ - "base", - "low", - "medium", - "high", - "best" - ], - "options_metadata": [], - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "toggle": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "str", - "value": "medium" - }, - "response": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "Response", - "dynamic": false, - "info": "The response to the user's query.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "response", - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "system_prompt": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "System Message", - "dynamic": false, - "info": "System-level instructions prepended to the user query.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "system_prompt", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - } - }, - "tool_mode": false - }, - "CleanlabRAGEvaluator": { - "base_classes": [ - "Data", - "dict", - "float", - "Message", - "number" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Evaluates context, query, and response from a RAG pipeline using Cleanlab and outputs trust metrics.", - "display_name": "Cleanlab RAG Evaluator", - "documentation": "", - "edited": false, - "field_order": [ - "api_key", - "model", - "quality_preset", - "context", - "query", - "response", - "run_context_sufficiency", - "run_response_groundedness", - "run_response_helpfulness", - "run_query_ease" - ], - "frozen": false, - "icon": "Cleanlab", - "legacy": false, - "metadata": { - "code_hash": "f48b57ff7ca3", - "dependencies": { - "dependencies": [ - { - "name": "cleanlab_tlm", - "version": "1.1.39" - }, - { - "name": "lfx", - "version": null - } - ], - "total_dependencies": 2 - }, - "module": "lfx.components.cleanlab.cleanlab_rag_evaluator.CleanlabRAGEvaluator" - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Response", - "group_outputs": false, - "method": "pass_response", - "name": "response_passthrough", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "Trust Score", - "group_outputs": false, - "method": "get_trust_score", - "name": "trust_score", - "selected": "number", - "tool_mode": true, - "types": [ - "number", - "float" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "Explanation", - "group_outputs": false, - "method": "get_trust_explanation", - "name": "trust_explanation", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "Other Evals", - "group_outputs": false, - "method": "get_other_scores", - "name": "other_scores", - "selected": "Data", - "tool_mode": true, - "types": [ - "Data", - "dict" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "Evaluation Summary", - "group_outputs": false, - "method": "get_evaluation_summary", - "name": "evaluation_summary", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "api_key": { - "_input_type": "SecretStrInput", - "advanced": false, - "display_name": "Cleanlab API Key", - "dynamic": false, - "info": "Your Cleanlab API key.", - "input_types": [], - "load_from_db": true, - "name": "api_key", - "override_skip": false, - "password": true, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from cleanlab_tlm import TrustworthyRAG, get_default_evals\n\nfrom lfx.custom import Component\nfrom lfx.io import (\n BoolInput,\n DropdownInput,\n MessageTextInput,\n Output,\n SecretStrInput,\n)\nfrom lfx.schema.message import Message\n\n\nclass CleanlabRAGEvaluator(Component):\n \"\"\"A component that evaluates the quality of RAG (Retrieval-Augmented Generation) outputs using Cleanlab.\n\n This component takes a query, retrieved context, and generated response from a RAG pipeline,\n and uses Cleanlab's evaluation algorithms to assess various aspects of the RAG system's performance.\n\n The component can evaluate:\n - Overall trustworthiness of the LLM generated response\n - Context sufficiency (whether the retrieved context contains information needed to answer the query)\n - Response groundedness (whether the response is supported directly by the context)\n - Response helpfulness (whether the response effectively addresses the user's query)\n - Query ease (whether the user query seems easy for an AI system to properly handle, useful to diagnose\n queries that are: complex, vague, tricky, or disgruntled-sounding)\n\n Outputs:\n - Trust Score: A score between 0-1 corresponding to the trustworthiness of the response. A higher score\n indicates a higher confidence that the response is correct/good.\n - Explanation: An LLM generated explanation of the trustworthiness assessment\n - Other Evals: Additional evaluation metrics for selected evaluation types in the \"Controls\" tab\n - Evaluation Summary: A comprehensive summary of context, query, response, and selected evaluation results\n\n This component works well in conjunction with the CleanlabRemediator to create a complete trust evaluation\n and remediation pipeline.\n\n More details on the evaluation metrics can be found here: https://help.cleanlab.ai/tlm/use-cases/tlm_rag/\n \"\"\"\n\n display_name = \"Cleanlab RAG Evaluator\"\n description = \"Evaluates context, query, and response from a RAG pipeline using Cleanlab and outputs trust metrics.\"\n icon = \"Cleanlab\"\n name = \"CleanlabRAGEvaluator\"\n\n inputs = [\n SecretStrInput(\n name=\"api_key\",\n display_name=\"Cleanlab API Key\",\n info=\"Your Cleanlab API key.\",\n required=True,\n ),\n DropdownInput(\n name=\"model\",\n display_name=\"Cleanlab Evaluation Model\",\n options=[\n \"gpt-4.1\",\n \"gpt-4.1-mini\",\n \"gpt-4.1-nano\",\n \"o4-mini\",\n \"o3\",\n \"gpt-4.5-preview\",\n \"gpt-4o-mini\",\n \"gpt-4o\",\n \"o3-mini\",\n \"o1\",\n \"o1-mini\",\n \"gpt-4\",\n \"gpt-3.5-turbo-16k\",\n \"claude-3.7-sonnet\",\n \"claude-3.5-sonnet-v2\",\n \"claude-3.5-sonnet\",\n \"claude-3.5-haiku\",\n \"claude-3-haiku\",\n \"nova-micro\",\n \"nova-lite\",\n \"nova-pro\",\n ],\n info=\"The model Cleanlab uses to evaluate the context, query, and response. This does NOT need to be \"\n \"the same model that generated the response.\",\n value=\"gpt-4o-mini\",\n required=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"quality_preset\",\n display_name=\"Quality Preset\",\n options=[\"base\", \"low\", \"medium\"],\n value=\"medium\",\n info=\"This determines the accuracy, latency, and cost of the evaluation. Higher quality is generally \"\n \"slower but more accurate.\",\n required=True,\n advanced=True,\n ),\n MessageTextInput(\n name=\"context\",\n display_name=\"Context\",\n info=\"The context retrieved for the given query.\",\n required=True,\n ),\n MessageTextInput(\n name=\"query\",\n display_name=\"Query\",\n info=\"The user's query.\",\n required=True,\n ),\n MessageTextInput(\n name=\"response\",\n display_name=\"Response\",\n info=\"The response generated by the LLM.\",\n required=True,\n ),\n BoolInput(\n name=\"run_context_sufficiency\",\n display_name=\"Run Context Sufficiency\",\n value=False,\n advanced=True,\n ),\n BoolInput(\n name=\"run_response_groundedness\",\n display_name=\"Run Response Groundedness\",\n value=False,\n advanced=True,\n ),\n BoolInput(\n name=\"run_response_helpfulness\",\n display_name=\"Run Response Helpfulness\",\n value=False,\n advanced=True,\n ),\n BoolInput(\n name=\"run_query_ease\",\n display_name=\"Run Query Ease\",\n value=False,\n advanced=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Response\", name=\"response_passthrough\", method=\"pass_response\", types=[\"Message\"]),\n Output(display_name=\"Trust Score\", name=\"trust_score\", method=\"get_trust_score\", types=[\"number\"]),\n Output(display_name=\"Explanation\", name=\"trust_explanation\", method=\"get_trust_explanation\", types=[\"Message\"]),\n Output(display_name=\"Other Evals\", name=\"other_scores\", method=\"get_other_scores\", types=[\"Data\"]),\n Output(\n display_name=\"Evaluation Summary\",\n name=\"evaluation_summary\",\n method=\"get_evaluation_summary\",\n types=[\"Message\"],\n ),\n ]\n\n def _evaluate_once(self):\n if not hasattr(self, \"_cached_result\"):\n try:\n self.status = \"Configuring selected evals...\"\n default_evals = get_default_evals()\n enabled_names = []\n if self.run_context_sufficiency:\n enabled_names.append(\"context_sufficiency\")\n if self.run_response_groundedness:\n enabled_names.append(\"response_groundedness\")\n if self.run_response_helpfulness:\n enabled_names.append(\"response_helpfulness\")\n if self.run_query_ease:\n enabled_names.append(\"query_ease\")\n\n selected_evals = [e for e in default_evals if e.name in enabled_names]\n\n validator = TrustworthyRAG(\n api_key=self.api_key,\n quality_preset=self.quality_preset,\n options={\"log\": [\"explanation\"], \"model\": self.model},\n evals=selected_evals,\n )\n\n self.status = f\"Running evals: {[e.name for e in selected_evals]}\"\n self._cached_result = validator.score(\n query=self.query,\n context=self.context,\n response=self.response,\n )\n self.status = \"Evaluation complete.\"\n\n except Exception as e: # noqa: BLE001\n self.status = f\"Evaluation failed: {e!s}\"\n self._cached_result = {}\n return self._cached_result\n\n def pass_response(self) -> Message:\n self.status = \"Passing through response.\"\n return Message(text=self.response)\n\n def get_trust_score(self) -> float:\n score = self._evaluate_once().get(\"trustworthiness\", {}).get(\"score\", 0.0)\n self.status = f\"Trust Score: {score:.3f}\"\n return score\n\n def get_trust_explanation(self) -> Message:\n explanation = self._evaluate_once().get(\"trustworthiness\", {}).get(\"log\", {}).get(\"explanation\", \"\")\n self.status = \"Trust explanation extracted.\"\n return Message(text=explanation)\n\n def get_other_scores(self) -> dict:\n result = self._evaluate_once()\n\n selected = {\n \"context_sufficiency\": self.run_context_sufficiency,\n \"response_groundedness\": self.run_response_groundedness,\n \"response_helpfulness\": self.run_response_helpfulness,\n \"query_ease\": self.run_query_ease,\n }\n\n filtered_scores = {key: result[key][\"score\"] for key, include in selected.items() if include and key in result}\n\n self.status = f\"{len(filtered_scores)} other evals returned.\"\n return filtered_scores\n\n def get_evaluation_summary(self) -> Message:\n result = self._evaluate_once()\n\n query_text = self.query.strip()\n context_text = self.context.strip()\n response_text = self.response.strip()\n\n trust = result.get(\"trustworthiness\", {}).get(\"score\", 0.0)\n trust_exp = result.get(\"trustworthiness\", {}).get(\"log\", {}).get(\"explanation\", \"\")\n\n selected = {\n \"context_sufficiency\": self.run_context_sufficiency,\n \"response_groundedness\": self.run_response_groundedness,\n \"response_helpfulness\": self.run_response_helpfulness,\n \"query_ease\": self.run_query_ease,\n }\n\n other_scores = {key: result[key][\"score\"] for key, include in selected.items() if include and key in result}\n\n metrics = f\"Trustworthiness: {trust:.3f}\"\n if trust_exp:\n metrics += f\"\\nExplanation: {trust_exp}\"\n if other_scores:\n metrics += \"\\n\" + \"\\n\".join(f\"{k.replace('_', ' ').title()}: {v:.3f}\" for k, v in other_scores.items())\n\n summary = (\n f\"Query:\\n{query_text}\\n\"\n \"-----\\n\"\n f\"Context:\\n{context_text}\\n\"\n \"-----\\n\"\n f\"Response:\\n{response_text}\\n\"\n \"------------------------------\\n\"\n f\"{metrics}\"\n )\n\n self.status = \"Evaluation summary built.\"\n return Message(text=summary)\n" - }, - "context": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "Context", - "dynamic": false, - "info": "The context retrieved for the given query.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "context", - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "model": { - "_input_type": "DropdownInput", - "advanced": true, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Cleanlab Evaluation Model", - "dynamic": false, - "external_options": {}, - "info": "The model Cleanlab uses to evaluate the context, query, and response. This does NOT need to be the same model that generated the response.", - "name": "model", - "options": [ - "gpt-4.1", - "gpt-4.1-mini", - "gpt-4.1-nano", - "o4-mini", - "o3", - "gpt-4.5-preview", - "gpt-4o-mini", - "gpt-4o", - "o3-mini", - "o1", - "o1-mini", - "gpt-4", - "gpt-3.5-turbo-16k", - "claude-3.7-sonnet", - "claude-3.5-sonnet-v2", - "claude-3.5-sonnet", - "claude-3.5-haiku", - "claude-3-haiku", - "nova-micro", - "nova-lite", - "nova-pro" - ], - "options_metadata": [], - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "toggle": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "str", - "value": "gpt-4o-mini" - }, - "quality_preset": { - "_input_type": "DropdownInput", - "advanced": true, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Quality Preset", - "dynamic": false, - "external_options": {}, - "info": "This determines the accuracy, latency, and cost of the evaluation. Higher quality is generally slower but more accurate.", - "name": "quality_preset", - "options": [ - "base", - "low", - "medium" - ], - "options_metadata": [], - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "toggle": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "str", - "value": "medium" - }, - "query": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "Query", - "dynamic": false, - "info": "The user's query.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "query", - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "response": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "Response", - "dynamic": false, - "info": "The response generated by the LLM.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "response", - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "run_context_sufficiency": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Run Context Sufficiency", - "dynamic": false, - "info": "", - "list": false, - "list_add_label": "Add More", - "name": "run_context_sufficiency", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "bool", - "value": false - }, - "run_query_ease": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Run Query Ease", - "dynamic": false, - "info": "", - "list": false, - "list_add_label": "Add More", - "name": "run_query_ease", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "bool", - "value": false - }, - "run_response_groundedness": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Run Response Groundedness", - "dynamic": false, - "info": "", - "list": false, - "list_add_label": "Add More", - "name": "run_response_groundedness", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "bool", - "value": false - }, - "run_response_helpfulness": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Run Response Helpfulness", - "dynamic": false, - "info": "", - "list": false, - "list_add_label": "Add More", - "name": "run_response_helpfulness", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "bool", - "value": false - } - }, - "tool_mode": false - }, - "CleanlabRemediator": { - "base_classes": [ - "Message" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Remediates an untrustworthy response based on trust score from the Cleanlab Evaluator, score threshold, and message handling settings.", - "display_name": "Cleanlab Remediator", - "documentation": "", - "edited": false, - "field_order": [ - "response", - "score", - "explanation", - "threshold", - "show_untrustworthy_response", - "untrustworthy_warning_text", - "fallback_text" - ], - "frozen": false, - "icon": "Cleanlab", - "legacy": false, - "metadata": { - "code_hash": "a5b19d338991", - "dependencies": { - "dependencies": [ - { - "name": "lfx", - "version": null - } - ], - "total_dependencies": 1 - }, - "module": "lfx.components.cleanlab.cleanlab_remediator.CleanlabRemediator" - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Remediated Message", - "group_outputs": false, - "method": "remediate_response", - "name": "remediated_response", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from lfx.custom import Component\nfrom lfx.field_typing.range_spec import RangeSpec\nfrom lfx.io import BoolInput, FloatInput, HandleInput, MessageTextInput, Output, PromptInput\nfrom lfx.schema.message import Message\n\n\nclass CleanlabRemediator(Component):\n \"\"\"Remediates potentially untrustworthy LLM responses based on trust scores computed by the Cleanlab Evaluator.\n\n This component takes a response and its associated trust score,\n and applies remediation strategies based on configurable thresholds and settings.\n\n Inputs:\n - response (MessageTextInput): The original LLM-generated response to be evaluated and possibly remediated.\n The CleanlabEvaluator passes this response through.\n - score (HandleInput): The trust score output from CleanlabEvaluator (expected to be a float between 0 and 1).\n - explanation (MessageTextInput): Optional textual explanation for the trust score, to be included in the\n output.\n - threshold (Input[float]): Minimum trust score required to accept the response. If the score is lower, the\n response is remediated.\n - show_untrustworthy_response (BoolInput): If true, returns the original response with a warning; if false,\n returns fallback text.\n - untrustworthy_warning_text (PromptInput): Text warning to append to responses deemed untrustworthy (when\n showing them).\n - fallback_text (PromptInput): Replacement message returned if the response is untrustworthy and should be\n hidden.\n\n Outputs:\n - remediated_response (Message): Either:\n • the original response,\n • the original response with appended warning, or\n • the fallback response,\n depending on the trust score and configuration.\n\n This component is typically used downstream of CleanlabEvaluator or CleanlabRagValidator\n to take appropriate action on low-trust responses and inform users accordingly.\n \"\"\"\n\n display_name = \"Cleanlab Remediator\"\n description = (\n \"Remediates an untrustworthy response based on trust score from the Cleanlab Evaluator, \"\n \"score threshold, and message handling settings.\"\n )\n icon = \"Cleanlab\"\n name = \"CleanlabRemediator\"\n\n inputs = [\n MessageTextInput(\n name=\"response\",\n display_name=\"Response\",\n info=\"The response to the user's query.\",\n required=True,\n ),\n HandleInput(\n name=\"score\",\n display_name=\"Trust Score\",\n info=\"The trustworthiness score output from the Cleanlab Evaluator.\",\n input_types=[\"number\"],\n required=True,\n ),\n MessageTextInput(\n name=\"explanation\",\n display_name=\"Explanation\",\n info=\"The explanation from the Cleanlab Evaluator.\",\n required=False,\n ),\n FloatInput(\n name=\"threshold\",\n display_name=\"Threshold\",\n field_type=\"float\",\n value=0.7,\n range_spec=RangeSpec(min=0.0, max=1.0, step=0.05),\n info=\"Minimum score required to show the response unmodified. Reponses with scores above this threshold \"\n \"are considered trustworthy. Reponses with scores below this threshold are considered untrustworthy and \"\n \"will be remediated based on the settings below.\",\n required=True,\n show=True,\n ),\n BoolInput(\n name=\"show_untrustworthy_response\",\n display_name=\"Show Untrustworthy Response\",\n info=\"If enabled, and the trust score is below the threshold, the original response is shown with the \"\n \"added warning. If disabled, and the trust score is below the threshold, the fallback answer is returned.\",\n value=True,\n ),\n PromptInput(\n name=\"untrustworthy_warning_text\",\n display_name=\"Warning for Untrustworthy Response\",\n info=\"Warning to append to the response if Show Untrustworthy Response is enabled and trust score is \"\n \"below the threshold.\",\n value=\"⚠️ WARNING: The following response is potentially untrustworthy.\",\n ),\n PromptInput(\n name=\"fallback_text\",\n display_name=\"Fallback Answer\",\n info=\"Response returned if the trust score is below the threshold and 'Show Untrustworthy Response' is \"\n \"disabled.\",\n value=\"Based on the available information, I cannot provide a complete answer to this question.\",\n ),\n ]\n\n outputs = [\n Output(\n display_name=\"Remediated Message\",\n name=\"remediated_response\",\n method=\"remediate_response\",\n types=[\"Message\"],\n ),\n ]\n\n def remediate_response(self) -> Message:\n if self.score >= self.threshold:\n self.status = f\"Score {self.score:.2f} ≥ threshold {self.threshold:.2f} → accepted\"\n return Message(\n text=f\"{self.response}\\n\\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n\\n**Trust Score:** {self.score:.2f}\"\n )\n\n self.status = f\"Score {self.score:.2f} < threshold {self.threshold:.2f} → flagged\"\n\n if self.show_untrustworthy_response:\n parts = [\n self.response,\n \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\",\n f\"**{self.untrustworthy_warning_text.strip()}**\",\n f\"**Trust Score:** {self.score:.2f}\",\n ]\n if self.explanation:\n parts.append(f\"**Explanation:** {self.explanation}\")\n return Message(text=\"\\n\\n\".join(parts))\n\n return Message(text=self.fallback_text)\n" - }, - "explanation": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "Explanation", - "dynamic": false, - "info": "The explanation from the Cleanlab Evaluator.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "explanation", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "fallback_text": { - "_input_type": "PromptInput", - "advanced": false, - "display_name": "Fallback Answer", - "dynamic": false, - "info": "Response returned if the trust score is below the threshold and 'Show Untrustworthy Response' is disabled.", - "list": false, - "list_add_label": "Add More", - "name": "fallback_text", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "track_in_telemetry": false, - "type": "prompt", - "value": "Based on the available information, I cannot provide a complete answer to this question." - }, - "response": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "Response", - "dynamic": false, - "info": "The response to the user's query.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "response", - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "score": { - "_input_type": "HandleInput", - "advanced": false, - "display_name": "Trust Score", - "dynamic": false, - "info": "The trustworthiness score output from the Cleanlab Evaluator.", - "input_types": [ - "number" - ], - "list": false, - "list_add_label": "Add More", - "name": "score", - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "other", - "value": "" - }, - "show_untrustworthy_response": { - "_input_type": "BoolInput", - "advanced": false, - "display_name": "Show Untrustworthy Response", - "dynamic": false, - "info": "If enabled, and the trust score is below the threshold, the original response is shown with the added warning. If disabled, and the trust score is below the threshold, the fallback answer is returned.", - "list": false, - "list_add_label": "Add More", - "name": "show_untrustworthy_response", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "bool", - "value": true - }, - "threshold": { - "_input_type": "FloatInput", - "advanced": false, - "display_name": "Threshold", - "dynamic": false, - "info": "Minimum score required to show the response unmodified. Reponses with scores above this threshold are considered trustworthy. Reponses with scores below this threshold are considered untrustworthy and will be remediated based on the settings below.", - "list": false, - "list_add_label": "Add More", - "name": "threshold", - "override_skip": false, - "placeholder": "", - "range_spec": { - "max": 1.0, - "min": 0.0, - "step": 0.05, - "step_type": "float" - }, - "required": true, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "float", - "value": 0.7 - }, - "untrustworthy_warning_text": { - "_input_type": "PromptInput", - "advanced": false, - "display_name": "Warning for Untrustworthy Response", - "dynamic": false, - "info": "Warning to append to the response if Show Untrustworthy Response is enabled and trust score is below the threshold.", - "list": false, - "list_add_label": "Add More", - "name": "untrustworthy_warning_text", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "track_in_telemetry": false, - "type": "prompt", - "value": "⚠️ WARNING: The following response is potentially untrustworthy." - } - }, - "tool_mode": false - } - } - ], [ "cloudflare", { @@ -63583,2110 +62665,6 @@ } } ], - [ - "jigsawstack", - { - "JigsawStackAIScraper": { - "base_classes": [ - "JSON" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Scrape any website instantly and get consistent structured data in seconds without writing any css selector code", - "display_name": "AI Scraper", - "documentation": "https://jigsawstack.com/docs/api-reference/ai/scrape", - "edited": false, - "field_order": [ - "api_key", - "url", - "html", - "element_prompts", - "root_element_selector" - ], - "frozen": false, - "icon": "JigsawStack", - "legacy": false, - "metadata": { - "code_hash": "08a0063e2564", - "dependencies": { - "dependencies": [ - { - "name": "lfx", - "version": null - }, - { - "name": "jigsawstack", - "version": "0.2.7" - } - ], - "total_dependencies": 2 - }, - "module": "lfx.components.jigsawstack.ai_scrape.JigsawStackAIScraperComponent" - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "AI Scraper Results", - "group_outputs": false, - "method": "scrape", - "name": "scrape_results", - "selected": "JSON", - "tool_mode": true, - "types": [ - "JSON" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "api_key": { - "_input_type": "SecretStrInput", - "advanced": false, - "display_name": "JigsawStack API Key", - "dynamic": false, - "info": "Your JigsawStack API key for authentication", - "input_types": [], - "load_from_db": true, - "name": "api_key", - "override_skip": false, - "password": true, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from lfx.custom.custom_component.component import Component\nfrom lfx.io import MessageTextInput, Output, SecretStrInput\nfrom lfx.schema.data import Data\n\nMAX_ELEMENT_PROMPTS = 5\n\n\nclass JigsawStackAIScraperComponent(Component):\n display_name = \"AI Scraper\"\n description = \"Scrape any website instantly and get consistent structured data \\\n in seconds without writing any css selector code\"\n documentation = \"https://jigsawstack.com/docs/api-reference/ai/scrape\"\n icon = \"JigsawStack\"\n name = \"JigsawStackAIScraper\"\n\n inputs = [\n SecretStrInput(\n name=\"api_key\",\n display_name=\"JigsawStack API Key\",\n info=\"Your JigsawStack API key for authentication\",\n required=True,\n ),\n MessageTextInput(\n name=\"url\",\n display_name=\"URL\",\n info=\"URL of the page to scrape. Either url or html is required, but not both.\",\n required=False,\n tool_mode=True,\n ),\n MessageTextInput(\n name=\"html\",\n display_name=\"HTML\",\n info=\"HTML content to scrape. Either url or html is required, but not both.\",\n required=False,\n tool_mode=True,\n ),\n MessageTextInput(\n name=\"element_prompts\",\n display_name=\"Element Prompts\",\n info=\"Items on the page to be scraped (maximum 5). E.g. 'Plan price', 'Plan title'\",\n required=True,\n tool_mode=True,\n ),\n MessageTextInput(\n name=\"root_element_selector\",\n display_name=\"Root Element Selector\",\n info=\"CSS selector to limit the scope of scraping to a specific element and its children\",\n required=False,\n value=\"main\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"AI Scraper Results\", name=\"scrape_results\", method=\"scrape\"),\n ]\n\n def scrape(self) -> Data:\n try:\n from jigsawstack import JigsawStack, JigsawStackError\n except ImportError as e:\n jigsawstack_import_error = (\n \"JigsawStack package not found. Please install it using: pip install jigsawstack>=0.2.7\"\n )\n raise ImportError(jigsawstack_import_error) from e\n\n try:\n client = JigsawStack(api_key=self.api_key)\n\n # Build request object\n scrape_params: dict = {}\n if self.url:\n scrape_params[\"url\"] = self.url\n if self.html:\n scrape_params[\"html\"] = self.html\n\n url_value = scrape_params.get(\"url\", \"\")\n html_value = scrape_params.get(\"html\", \"\")\n if (not url_value or not url_value.strip()) and (not html_value or not html_value.strip()):\n url_or_html_error = \"Either 'url' or 'html' must be provided for scraping\"\n raise ValueError(url_or_html_error)\n\n # Process element_prompts with proper type handling\n element_prompts_list: list[str] = []\n if self.element_prompts:\n element_prompts_value: str | list[str] = self.element_prompts\n\n if isinstance(element_prompts_value, str):\n if \",\" not in element_prompts_value:\n element_prompts_list = [element_prompts_value]\n else:\n element_prompts_list = element_prompts_value.split(\",\")\n elif isinstance(element_prompts_value, list):\n element_prompts_list = element_prompts_value\n else:\n # Fallback for other types\n element_prompts_list = str(element_prompts_value).split(\",\")\n\n if len(element_prompts_list) > MAX_ELEMENT_PROMPTS:\n max_elements_error = \"Maximum of 5 element prompts allowed\"\n raise ValueError(max_elements_error)\n if len(element_prompts_list) == 0:\n invalid_elements_error = \"Element prompts cannot be empty\"\n raise ValueError(invalid_elements_error)\n\n scrape_params[\"element_prompts\"] = element_prompts_list\n\n if self.root_element_selector:\n scrape_params[\"root_element_selector\"] = self.root_element_selector\n\n # Call web scraping\n response = client.web.ai_scrape(scrape_params)\n\n if not response.get(\"success\", False):\n fail_error = \"JigsawStack API request failed.\"\n raise ValueError(fail_error)\n\n result_data = response\n\n self.status = \"AI scrape process is now complete.\"\n\n return Data(data=result_data)\n\n except JigsawStackError as e:\n error_data = {\"error\": str(e), \"success\": False}\n self.status = f\"Error: {e!s}\"\n return Data(data=error_data)\n" - }, - "element_prompts": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "Element Prompts", - "dynamic": false, - "info": "Items on the page to be scraped (maximum 5). E.g. 'Plan price', 'Plan title'", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "element_prompts", - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "html": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "HTML", - "dynamic": false, - "info": "HTML content to scrape. Either url or html is required, but not both.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "html", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "root_element_selector": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "Root Element Selector", - "dynamic": false, - "info": "CSS selector to limit the scope of scraping to a specific element and its children", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "root_element_selector", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "main" - }, - "url": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "URL", - "dynamic": false, - "info": "URL of the page to scrape. Either url or html is required, but not both.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "url", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - } - }, - "tool_mode": false - }, - "JigsawStackAISearch": { - "base_classes": [ - "JSON", - "Message" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Effortlessly search the Web and get access to high-quality results powered with AI.", - "display_name": "AI Web Search", - "documentation": "https://jigsawstack.com/docs/api-reference/web/ai-search", - "edited": false, - "field_order": [ - "api_key", - "query", - "ai_overview", - "safe_search", - "spell_check" - ], - "frozen": false, - "icon": "JigsawStack", - "legacy": false, - "metadata": { - "code_hash": "5e821ee44040", - "dependencies": { - "dependencies": [ - { - "name": "lfx", - "version": null - }, - { - "name": "jigsawstack", - "version": "0.2.7" - } - ], - "total_dependencies": 2 - }, - "module": "lfx.components.jigsawstack.ai_web_search.JigsawStackAIWebSearchComponent" - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "AI Search Results", - "group_outputs": false, - "method": "search", - "name": "search_results", - "selected": "JSON", - "tool_mode": true, - "types": [ - "JSON" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "Content Text", - "group_outputs": false, - "method": "get_content_text", - "name": "content_text", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "ai_overview": { - "_input_type": "BoolInput", - "advanced": false, - "display_name": "AI Overview", - "dynamic": false, - "info": "Include AI powered overview in the search results", - "list": false, - "list_add_label": "Add More", - "name": "ai_overview", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "bool", - "value": true - }, - "api_key": { - "_input_type": "SecretStrInput", - "advanced": false, - "display_name": "JigsawStack API Key", - "dynamic": false, - "info": "Your JigsawStack API key for authentication", - "input_types": [], - "load_from_db": true, - "name": "api_key", - "override_skip": false, - "password": true, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from lfx.custom.custom_component.component import Component\nfrom lfx.io import BoolInput, DropdownInput, Output, QueryInput, SecretStrInput\nfrom lfx.schema.data import Data\nfrom lfx.schema.message import Message\n\n\nclass JigsawStackAIWebSearchComponent(Component):\n display_name = \"AI Web Search\"\n description = \"Effortlessly search the Web and get access to high-quality results powered with AI.\"\n documentation = \"https://jigsawstack.com/docs/api-reference/web/ai-search\"\n icon = \"JigsawStack\"\n name = \"JigsawStackAISearch\"\n inputs = [\n SecretStrInput(\n name=\"api_key\",\n display_name=\"JigsawStack API Key\",\n info=\"Your JigsawStack API key for authentication\",\n required=True,\n ),\n QueryInput(\n name=\"query\",\n display_name=\"Query\",\n info=\"The search value. The maximum query character length is 400\",\n required=True,\n tool_mode=True,\n ),\n BoolInput(\n name=\"ai_overview\",\n display_name=\"AI Overview\",\n info=\"Include AI powered overview in the search results\",\n required=False,\n value=True,\n ),\n DropdownInput(\n name=\"safe_search\",\n display_name=\"Safe Search\",\n info=\"Enable safe search to filter out adult content\",\n required=False,\n options=[\"moderate\", \"strict\", \"off\"],\n value=\"off\",\n ),\n BoolInput(\n name=\"spell_check\",\n display_name=\"Spell Check\",\n info=\"Spell check the search query\",\n required=False,\n value=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"AI Search Results\", name=\"search_results\", method=\"search\"),\n Output(display_name=\"Content Text\", name=\"content_text\", method=\"get_content_text\"),\n ]\n\n def search(self) -> Data:\n try:\n from jigsawstack import JigsawStack, JigsawStackError\n except ImportError as e:\n jigsawstack_import_error = (\n \"JigsawStack package not found. Please install it using: pip install jigsawstack>=0.2.7\"\n )\n raise ImportError(jigsawstack_import_error) from e\n\n try:\n client = JigsawStack(api_key=self.api_key)\n\n # build request object\n search_params = {}\n if self.query:\n search_params[\"query\"] = self.query\n if self.ai_overview is not None:\n search_params[\"ai_overview\"] = self.ai_overview\n if self.safe_search:\n search_params[\"safe_search\"] = self.safe_search\n if self.spell_check is not None:\n search_params[\"spell_check\"] = self.spell_check\n\n # Call web scraping\n response = client.web.search(search_params)\n\n api_error_msg = \"JigsawStack API returned unsuccessful response\"\n if not response.get(\"success\", False):\n raise ValueError(api_error_msg)\n\n # Create comprehensive data object\n result_data = {\n \"query\": self.query,\n \"ai_overview\": response.get(\"ai_overview\", \"\"),\n \"spell_fixed\": response.get(\"spell_fixed\", False),\n \"is_safe\": response.get(\"is_safe\", True),\n \"results\": response.get(\"results\", []),\n \"success\": True,\n }\n\n self.status = f\"Search complete for: {response.get('query', '')}\"\n\n return Data(data=result_data)\n\n except JigsawStackError as e:\n error_data = {\"error\": str(e), \"success\": False}\n self.status = f\"Error: {e!s}\"\n return Data(data=error_data)\n\n def get_content_text(self) -> Message:\n try:\n from jigsawstack import JigsawStack, JigsawStackError\n except ImportError:\n return Message(text=\"Error: JigsawStack package not found.\")\n\n try:\n # Initialize JigsawStack client\n client = JigsawStack(api_key=self.api_key)\n search_params = {}\n if self.query:\n search_params[\"query\"] = self.query\n if self.ai_overview is not None:\n search_params[\"ai_overview\"] = self.ai_overview\n if self.safe_search:\n search_params[\"safe_search\"] = self.safe_search\n if self.spell_check is not None:\n search_params[\"spell_check\"] = self.spell_check\n\n # Call web scraping\n response = client.web.search(search_params)\n\n request_failed_msg = \"Request Failed\"\n if not response.get(\"success\", False):\n raise JigsawStackError(request_failed_msg)\n\n # Return the content as text\n content = response.get(\"ai_overview\", \"\")\n return Message(text=content)\n\n except JigsawStackError as e:\n return Message(text=f\"Error while using AI Search: {e!s}\")\n" - }, - "query": { - "_input_type": "QueryInput", - "advanced": false, - "display_name": "Query", - "dynamic": false, - "info": "The search value. The maximum query character length is 400", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "query", - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "query", - "value": "" - }, - "safe_search": { - "_input_type": "DropdownInput", - "advanced": false, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Safe Search", - "dynamic": false, - "external_options": {}, - "info": "Enable safe search to filter out adult content", - "name": "safe_search", - "options": [ - "moderate", - "strict", - "off" - ], - "options_metadata": [], - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "toggle": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "str", - "value": "off" - }, - "spell_check": { - "_input_type": "BoolInput", - "advanced": false, - "display_name": "Spell Check", - "dynamic": false, - "info": "Spell check the search query", - "list": false, - "list_add_label": "Add More", - "name": "spell_check", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "bool", - "value": true - } - }, - "tool_mode": false - }, - "JigsawStackFileRead": { - "base_classes": [ - "JSON" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Read any previously uploaded file seamlessly from JigsawStack File Storage and use it in your AI applications.", - "display_name": "File Read", - "documentation": "https://jigsawstack.com/docs/api-reference/store/file/get", - "edited": false, - "field_order": [ - "api_key", - "key" - ], - "frozen": false, - "icon": "JigsawStack", - "legacy": false, - "metadata": { - "code_hash": "84d122f46fb1", - "dependencies": { - "dependencies": [ - { - "name": "lfx", - "version": null - }, - { - "name": "jigsawstack", - "version": "0.2.7" - } - ], - "total_dependencies": 2 - }, - "module": "lfx.components.jigsawstack.file_read.JigsawStackFileReadComponent" - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "File Path", - "group_outputs": false, - "method": "read_and_save_file", - "name": "file_path", - "selected": "JSON", - "tool_mode": true, - "types": [ - "JSON" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "api_key": { - "_input_type": "SecretStrInput", - "advanced": false, - "display_name": "JigsawStack API Key", - "dynamic": false, - "info": "Your JigsawStack API key for authentication", - "input_types": [], - "load_from_db": true, - "name": "api_key", - "override_skip": false, - "password": true, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "import tempfile\n\nfrom lfx.custom.custom_component.component import Component\nfrom lfx.io import Output, SecretStrInput, StrInput\nfrom lfx.schema.data import Data\n\n\nclass JigsawStackFileReadComponent(Component):\n display_name = \"File Read\"\n description = \"Read any previously uploaded file seamlessly from \\\n JigsawStack File Storage and use it in your AI applications.\"\n documentation = \"https://jigsawstack.com/docs/api-reference/store/file/get\"\n icon = \"JigsawStack\"\n name = \"JigsawStackFileRead\"\n\n inputs = [\n SecretStrInput(\n name=\"api_key\",\n display_name=\"JigsawStack API Key\",\n info=\"Your JigsawStack API key for authentication\",\n required=True,\n ),\n StrInput(\n name=\"key\",\n display_name=\"Key\",\n info=\"The key used to retrieve the file from JigsawStack File Storage.\",\n required=True,\n tool_mode=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"File Path\", name=\"file_path\", method=\"read_and_save_file\"),\n ]\n\n def read_and_save_file(self) -> Data:\n \"\"\"Read file from JigsawStack and save to temp file, return file path.\"\"\"\n try:\n from jigsawstack import JigsawStack, JigsawStackError\n except ImportError as e:\n jigsawstack_import_error = (\n \"JigsawStack package not found. Please install it using: pip install jigsawstack>=0.2.7\"\n )\n raise ImportError(jigsawstack_import_error) from e\n\n try:\n client = JigsawStack(api_key=self.api_key)\n if not self.key or self.key.strip() == \"\":\n invalid_key_error = \"Key is required to read a file from JigsawStack File Storage.\"\n raise ValueError(invalid_key_error)\n\n # Download file content\n response = client.store.get(self.key)\n\n # Determine file extension\n file_extension = self._detect_file_extension(response)\n\n # Create temporary file\n with tempfile.NamedTemporaryFile(\n delete=False, suffix=file_extension, prefix=f\"jigsawstack_{self.key}_\"\n ) as temp_file:\n if isinstance(response, bytes):\n temp_file.write(response)\n else:\n # Handle string content\n temp_file.write(response.encode(\"utf-8\"))\n\n temp_path = temp_file.name\n\n return Data(\n data={\n \"file_path\": temp_path,\n \"key\": self.key,\n \"file_extension\": file_extension,\n \"size\": len(response) if isinstance(response, bytes) else len(str(response)),\n \"success\": True,\n }\n )\n\n except JigsawStackError as e:\n error_data = {\"error\": str(e), \"success\": False}\n self.status = f\"Error: {e!s}\"\n return Data(data=error_data)\n\n def _detect_file_extension(self, content) -> str:\n \"\"\"Detect file extension based on content headers.\"\"\"\n if isinstance(content, bytes):\n # Check magic numbers for common file types\n if content.startswith(b\"\\xff\\xd8\\xff\"):\n return \".jpg\"\n if content.startswith(b\"\\x89PNG\\r\\n\\x1a\\n\"):\n return \".png\"\n if content.startswith((b\"GIF87a\", b\"GIF89a\")):\n return \".gif\"\n if content.startswith(b\"%PDF\"):\n return \".pdf\"\n if content.startswith(b\"PK\\x03\\x04\"): # ZIP/DOCX/XLSX\n return \".zip\"\n if content.startswith(b\"\\x00\\x00\\x01\\x00\"): # ICO\n return \".ico\"\n if content.startswith(b\"RIFF\") and b\"WEBP\" in content[:12]:\n return \".webp\"\n if content.startswith((b\"\\xff\\xfb\", b\"\\xff\\xf3\", b\"\\xff\\xf2\")):\n return \".mp3\"\n if content.startswith((b\"ftypmp4\", b\"\\x00\\x00\\x00\\x20ftypmp4\")):\n return \".mp4\"\n # Try to decode as text\n try:\n content.decode(\"utf-8\")\n return \".txt\" # noqa: TRY300\n except UnicodeDecodeError:\n return \".bin\" # Binary file\n else:\n # String content\n return \".txt\"\n" - }, - "key": { - "_input_type": "StrInput", - "advanced": false, - "display_name": "Key", - "dynamic": false, - "info": "The key used to retrieve the file from JigsawStack File Storage.", - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "key", - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - } - }, - "tool_mode": false - }, - "JigsawStackFileUpload": { - "base_classes": [ - "JSON" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Store any file seamlessly on JigsawStack File Storage and use it in your AI applications. Supports various file types including images, documents, and more.", - "display_name": "File Upload", - "documentation": "https://jigsawstack.com/docs/api-reference/store/file/add", - "edited": false, - "field_order": [ - "api_key", - "file", - "key", - "overwrite", - "temp_public_url" - ], - "frozen": false, - "icon": "JigsawStack", - "legacy": false, - "metadata": { - "code_hash": "8261dc72a655", - "dependencies": { - "dependencies": [ - { - "name": "lfx", - "version": null - }, - { - "name": "jigsawstack", - "version": "0.2.7" - } - ], - "total_dependencies": 2 - }, - "module": "lfx.components.jigsawstack.file_upload.JigsawStackFileUploadComponent" - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "File Store Result", - "group_outputs": false, - "method": "upload_file", - "name": "file_upload_result", - "selected": "JSON", - "tool_mode": true, - "types": [ - "JSON" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "api_key": { - "_input_type": "SecretStrInput", - "advanced": false, - "display_name": "JigsawStack API Key", - "dynamic": false, - "info": "Your JigsawStack API key for authentication", - "input_types": [], - "load_from_db": true, - "name": "api_key", - "override_skip": false, - "password": true, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from pathlib import Path\n\nfrom lfx.custom.custom_component.component import Component\nfrom lfx.io import BoolInput, FileInput, Output, SecretStrInput, StrInput\nfrom lfx.schema.data import Data\n\n\nclass JigsawStackFileUploadComponent(Component):\n display_name = \"File Upload\"\n description = \"Store any file seamlessly on JigsawStack File Storage and use it in your AI applications. \\\n Supports various file types including images, documents, and more.\"\n documentation = \"https://jigsawstack.com/docs/api-reference/store/file/add\"\n icon = \"JigsawStack\"\n name = \"JigsawStackFileUpload\"\n\n inputs = [\n SecretStrInput(\n name=\"api_key\",\n display_name=\"JigsawStack API Key\",\n info=\"Your JigsawStack API key for authentication\",\n required=True,\n ),\n FileInput(\n name=\"file\",\n display_name=\"File\",\n info=\"Upload file to be stored on JigsawStack File Storage.\",\n required=True,\n file_types=[\"pdf\", \"png\", \"jpg\", \"jpeg\", \"mp4\", \"mp3\", \"txt\", \"docx\", \"xlsx\"],\n ),\n StrInput(\n name=\"key\",\n display_name=\"Key\",\n info=\"The key used to store the file on JigsawStack File Storage. \\\n If not provided, a unique key will be generated.\",\n required=False,\n tool_mode=True,\n ),\n BoolInput(\n name=\"overwrite\",\n display_name=\"Overwrite Existing File\",\n info=\"If true, will overwrite the existing file with the same key. \\\n If false, will return an error if the file already exists.\",\n required=False,\n value=True,\n ),\n BoolInput(\n name=\"temp_public_url\",\n display_name=\"Return Temporary Public URL\",\n info=\"If true, will return a temporary public URL which lasts for a limited time. \\\n If false, will return the file store key which can only be accessed by the owner.\",\n required=False,\n value=False,\n tool_mode=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"File Store Result\", name=\"file_upload_result\", method=\"upload_file\"),\n ]\n\n def upload_file(self) -> Data:\n try:\n from jigsawstack import JigsawStack, JigsawStackError\n except ImportError as e:\n jigsawstack_import_error = (\n \"JigsawStack package not found. Please install it using: pip install jigsawstack>=0.2.7\"\n )\n raise ImportError(jigsawstack_import_error) from e\n\n try:\n client = JigsawStack(api_key=self.api_key)\n\n file_path = Path(self.file)\n with Path.open(file_path, \"rb\") as f:\n file_content = f.read()\n params = {}\n\n if self.key:\n # if key is provided, use it as the file store key\n params[\"key\"] = self.key\n if self.overwrite is not None:\n # if overwrite is provided, use it to determine if the file should be overwritten\n params[\"overwrite\"] = self.overwrite\n if self.temp_public_url is not None:\n # if temp_public_url is provided, use it to determine if a temporary public URL should\n params[\"temp_public_url\"] = self.temp_public_url\n\n response = client.store.upload(file_content, params)\n return Data(data=response)\n\n except JigsawStackError as e:\n error_data = {\"error\": str(e), \"success\": False}\n self.status = f\"Error: {e!s}\"\n return Data(data=error_data)\n" - }, - "file": { - "_input_type": "FileInput", - "advanced": false, - "display_name": "File", - "dynamic": false, - "fileTypes": [ - "pdf", - "png", - "jpg", - "jpeg", - "mp4", - "mp3", - "txt", - "docx", - "xlsx" - ], - "file_path": "", - "info": "Upload file to be stored on JigsawStack File Storage.", - "list": false, - "list_add_label": "Add More", - "name": "file", - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "temp_file": false, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "file", - "value": "" - }, - "key": { - "_input_type": "StrInput", - "advanced": false, - "display_name": "Key", - "dynamic": false, - "info": "The key used to store the file on JigsawStack File Storage. If not provided, a unique key will be generated.", - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "key", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "overwrite": { - "_input_type": "BoolInput", - "advanced": false, - "display_name": "Overwrite Existing File", - "dynamic": false, - "info": "If true, will overwrite the existing file with the same key. If false, will return an error if the file already exists.", - "list": false, - "list_add_label": "Add More", - "name": "overwrite", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "bool", - "value": true - }, - "temp_public_url": { - "_input_type": "BoolInput", - "advanced": false, - "display_name": "Return Temporary Public URL", - "dynamic": false, - "info": "If true, will return a temporary public URL which lasts for a limited time. If false, will return the file store key which can only be accessed by the owner.", - "list": false, - "list_add_label": "Add More", - "name": "temp_public_url", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "bool", - "value": false - } - }, - "tool_mode": false - }, - "JigsawStackImageGeneration": { - "base_classes": [ - "JSON" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Generate an image based on the given text by employing AI models like Flux, Stable Diffusion, and other top models.", - "display_name": "Image Generation", - "documentation": "https://jigsawstack.com/docs/api-reference/ai/image-generation", - "edited": false, - "field_order": [ - "api_key", - "prompt", - "aspect_ratio", - "url", - "file_store_key", - "width", - "height", - "steps", - "output_format", - "negative_prompt", - "seed", - "guidance" - ], - "frozen": false, - "icon": "JigsawStack", - "legacy": false, - "metadata": { - "code_hash": "463f8efbaa18", - "dependencies": { - "dependencies": [ - { - "name": "lfx", - "version": null - }, - { - "name": "jigsawstack", - "version": "0.2.7" - } - ], - "total_dependencies": 2 - }, - "module": "lfx.components.jigsawstack.image_generation.JigsawStackImageGenerationComponent" - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Image Generation Results", - "group_outputs": false, - "method": "generate_image", - "name": "image_generation_results", - "selected": "JSON", - "tool_mode": true, - "types": [ - "JSON" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "api_key": { - "_input_type": "SecretStrInput", - "advanced": false, - "display_name": "JigsawStack API Key", - "dynamic": false, - "info": "Your JigsawStack API key for authentication", - "input_types": [], - "load_from_db": true, - "name": "api_key", - "override_skip": false, - "password": true, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "aspect_ratio": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "Aspect Ratio", - "dynamic": false, - "info": "The aspect ratio of the generated image. Must be one of the following: '1:1', '16:9', '21:9', '3:2', '2:3', '4:5', '5:4', '3:4', '4:3', '9:16', '9:21' Default is 1:1.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "aspect_ratio", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from lfx.custom.custom_component.component import Component\nfrom lfx.io import DropdownInput, IntInput, MessageTextInput, Output, SecretStrInput\nfrom lfx.schema.data import Data\n\n\nclass JigsawStackImageGenerationComponent(Component):\n display_name = \"Image Generation\"\n description = \"Generate an image based on the given text by employing AI models like Flux, \\\n Stable Diffusion, and other top models.\"\n documentation = \"https://jigsawstack.com/docs/api-reference/ai/image-generation\"\n icon = \"JigsawStack\"\n name = \"JigsawStackImageGeneration\"\n\n inputs = [\n SecretStrInput(\n name=\"api_key\",\n display_name=\"JigsawStack API Key\",\n info=\"Your JigsawStack API key for authentication\",\n required=True,\n ),\n MessageTextInput(\n name=\"prompt\",\n display_name=\"Prompt\",\n info=\"The text prompt to generate the image from. Must be between 1-5000 characters.\",\n required=True,\n tool_mode=True,\n ),\n MessageTextInput(\n name=\"aspect_ratio\",\n display_name=\"Aspect Ratio\",\n info=\"The aspect ratio of the generated image. Must be one of the following:\\\n '1:1', '16:9', '21:9', '3:2', '2:3', '4:5', '5:4', '3:4', '4:3', '9:16', '9:21' \\\n Default is 1:1.\",\n required=False,\n tool_mode=True,\n ),\n MessageTextInput(\n name=\"url\",\n display_name=\"URL\",\n info=\"A valid URL where the generated image will be sent.\",\n required=False,\n ),\n MessageTextInput(\n name=\"file_store_key\",\n display_name=\"File Store Key\",\n info=\"The key used to store the image on Jigsawstack File Storage. Not required if url is specified.\",\n required=False,\n tool_mode=True,\n ),\n IntInput(\n name=\"width\",\n display_name=\"Width\",\n info=\"The width of the image. Must be between 256-1920 pixels.\",\n required=False,\n ),\n IntInput(\n name=\"height\",\n display_name=\"Height\",\n info=\"The height of the image. Must be between 256-1920 pixels.\",\n required=False,\n ),\n IntInput(\n name=\"steps\",\n display_name=\"Steps\",\n info=\"The number of denoising steps. Must be between 1-90. \\\n Higher values produce better quality images but take more time to generate.\",\n required=False,\n ),\n DropdownInput(\n name=\"output_format\",\n display_name=\"Output Format\",\n info=\"The output format of the generated image. Must be one of the following values:\\\n png or svg\",\n required=False,\n options=[\"png\", \"svg\"],\n value=\"png\",\n ),\n MessageTextInput(\n name=\"negative_prompt\",\n display_name=\"Negative Prompt\",\n info=\"The text prompt to avoid in the generated image. \\\n Must be between 1-5000 characters.\",\n required=False,\n tool_mode=True,\n advanced=True,\n ),\n IntInput(\n name=\"seed\",\n display_name=\"Seed\",\n info=\"Makes generation deterministic.\\\n Using the same seed and set of parameters will produce identical image each time.\",\n required=False,\n tool_mode=True,\n advanced=True,\n ),\n IntInput(\n name=\"guidance\",\n display_name=\"Guidance Scale\",\n info=\"Higher guidance forces the model to better follow the prompt, \\\n but may result in lower quality output. Must be between 1-28.\",\n required=False,\n tool_mode=True,\n advanced=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Image Generation Results\", name=\"image_generation_results\", method=\"generate_image\"),\n ]\n\n def generate_image(self) -> Data:\n try:\n from jigsawstack import JigsawStack, JigsawStackError\n except ImportError as e:\n jigsawstack_import_error = (\n \"JigsawStack package not found. Please install it using: pip install jigsawstack>=0.2.7\"\n )\n raise ImportError(jigsawstack_import_error) from e\n\n try:\n min_character_length = 1\n max_character_length = 5000\n min_width = 256\n max_width = 1920\n min_height = 256\n max_height = 1920\n min_steps = 1\n max_steps = 90\n client = JigsawStack(api_key=self.api_key)\n\n if not self.prompt or len(self.prompt) < min_character_length or len(self.prompt) > max_character_length:\n invalid_prompt_error = f\"Prompts must be between \\\n {min_character_length}-{max_character_length} characters.\"\n raise ValueError(invalid_prompt_error)\n\n if self.aspect_ratio and self.aspect_ratio not in [\n \"1:1\",\n \"16:9\",\n \"21:9\",\n \"3:2\",\n \"2:3\",\n \"4:5\",\n \"5:4\",\n \"3:4\",\n \"4:3\",\n \"9:16\",\n \"9:21\",\n ]:\n invalid_aspect_ratio_error = (\n \"Aspect ratio must be one of the following: '1:1', '16:9', '21:9', '3:2', '2:3', \"\n \"'4:5', '5:4', '3:4', '4:3', '9:16', '9:21'.\"\n )\n raise ValueError(invalid_aspect_ratio_error)\n if self.width and (self.width < min_width or self.width > max_width):\n invalid_width_error = f\"Width must be between {min_width}-{max_width} pixels.\"\n raise ValueError(invalid_width_error)\n if self.height and (self.height < min_height or self.height > max_height):\n invalid_height_error = f\"Height must be between {min_height}-{max_height} pixels.\"\n raise ValueError(invalid_height_error)\n if self.steps and (self.steps < min_steps or self.steps > max_steps):\n invalid_steps_error = f\"Steps must be between {min_steps}-{max_steps}.\"\n raise ValueError(invalid_steps_error)\n\n params = {}\n if self.prompt:\n params[\"prompt\"] = self.prompt.strip()\n if self.aspect_ratio:\n params[\"aspect_ratio\"] = self.aspect_ratio.strip()\n if self.url:\n params[\"url\"] = self.url.strip()\n if self.file_store_key:\n params[\"file_store_key\"] = self.file_store_key.strip()\n if self.width:\n params[\"width\"] = self.width\n if self.height:\n params[\"height\"] = self.height\n params[\"return_type\"] = \"url\"\n if self.output_format:\n params[\"output_format\"] = self.output_format.strip()\n if self.steps:\n params[\"steps\"] = self.steps\n\n # Initialize advance_config if any advanced parameters are provided\n if self.negative_prompt or self.seed or self.guidance:\n params[\"advance_config\"] = {}\n if self.negative_prompt:\n params[\"advance_config\"][\"negative_prompt\"] = self.negative_prompt\n if self.seed:\n params[\"advance_config\"][\"seed\"] = self.seed\n if self.guidance:\n params[\"advance_config\"][\"guidance\"] = self.guidance\n\n # Call image generation\n response = client.image_generation(params)\n\n if response.get(\"url\", None) is None or response.get(\"url\", None).strip() == \"\":\n failed_response_error = \"JigsawStack API returned unsuccessful response\"\n raise ValueError(failed_response_error)\n\n return Data(data=response)\n\n except JigsawStackError as e:\n error_data = {\"error\": str(e), \"success\": False}\n self.status = f\"Error: {e!s}\"\n return Data(data=error_data)\n" - }, - "file_store_key": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "File Store Key", - "dynamic": false, - "info": "The key used to store the image on Jigsawstack File Storage. Not required if url is specified.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "file_store_key", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "guidance": { - "_input_type": "IntInput", - "advanced": true, - "display_name": "Guidance Scale", - "dynamic": false, - "info": "Higher guidance forces the model to better follow the prompt, but may result in lower quality output. Must be between 1-28.", - "list": false, - "list_add_label": "Add More", - "name": "guidance", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "int", - "value": 0 - }, - "height": { - "_input_type": "IntInput", - "advanced": false, - "display_name": "Height", - "dynamic": false, - "info": "The height of the image. Must be between 256-1920 pixels.", - "list": false, - "list_add_label": "Add More", - "name": "height", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "int", - "value": 0 - }, - "negative_prompt": { - "_input_type": "MessageTextInput", - "advanced": true, - "display_name": "Negative Prompt", - "dynamic": false, - "info": "The text prompt to avoid in the generated image. Must be between 1-5000 characters.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "negative_prompt", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "output_format": { - "_input_type": "DropdownInput", - "advanced": false, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Output Format", - "dynamic": false, - "external_options": {}, - "info": "The output format of the generated image. Must be one of the following values: png or svg", - "name": "output_format", - "options": [ - "png", - "svg" - ], - "options_metadata": [], - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "toggle": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "str", - "value": "png" - }, - "prompt": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "Prompt", - "dynamic": false, - "info": "The text prompt to generate the image from. Must be between 1-5000 characters.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "prompt", - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "seed": { - "_input_type": "IntInput", - "advanced": true, - "display_name": "Seed", - "dynamic": false, - "info": "Makes generation deterministic. Using the same seed and set of parameters will produce identical image each time.", - "list": false, - "list_add_label": "Add More", - "name": "seed", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "int", - "value": 0 - }, - "steps": { - "_input_type": "IntInput", - "advanced": false, - "display_name": "Steps", - "dynamic": false, - "info": "The number of denoising steps. Must be between 1-90. Higher values produce better quality images but take more time to generate.", - "list": false, - "list_add_label": "Add More", - "name": "steps", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "int", - "value": 0 - }, - "url": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "URL", - "dynamic": false, - "info": "A valid URL where the generated image will be sent.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "url", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "width": { - "_input_type": "IntInput", - "advanced": false, - "display_name": "Width", - "dynamic": false, - "info": "The width of the image. Must be between 256-1920 pixels.", - "list": false, - "list_add_label": "Add More", - "name": "width", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "int", - "value": 0 - } - }, - "tool_mode": false - }, - "JigsawStackNSFW": { - "base_classes": [ - "JSON" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Detect if image/video contains NSFW content", - "display_name": "NSFW Detection", - "documentation": "https://jigsawstack.com/docs/api-reference/ai/nsfw", - "edited": false, - "field_order": [ - "api_key", - "url" - ], - "frozen": false, - "icon": "JigsawStack", - "legacy": false, - "metadata": { - "code_hash": "88b5eee38906", - "dependencies": { - "dependencies": [ - { - "name": "lfx", - "version": null - }, - { - "name": "jigsawstack", - "version": "0.2.7" - } - ], - "total_dependencies": 2 - }, - "module": "lfx.components.jigsawstack.nsfw.JigsawStackNSFWComponent" - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "NSFW Analysis", - "group_outputs": false, - "method": "detect_nsfw", - "name": "nsfw_result", - "selected": "JSON", - "tool_mode": true, - "types": [ - "JSON" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "api_key": { - "_input_type": "SecretStrInput", - "advanced": false, - "display_name": "JigsawStack API Key", - "dynamic": false, - "info": "Your JigsawStack API key for authentication", - "input_types": [], - "load_from_db": true, - "name": "api_key", - "override_skip": false, - "password": true, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from lfx.custom.custom_component.component import Component\nfrom lfx.io import Output, SecretStrInput, StrInput\nfrom lfx.schema.data import Data\n\n\nclass JigsawStackNSFWComponent(Component):\n display_name = \"NSFW Detection\"\n description = \"Detect if image/video contains NSFW content\"\n documentation = \"https://jigsawstack.com/docs/api-reference/ai/nsfw\"\n icon = \"JigsawStack\"\n name = \"JigsawStackNSFW\"\n\n inputs = [\n SecretStrInput(\n name=\"api_key\",\n display_name=\"JigsawStack API Key\",\n info=\"Your JigsawStack API key for authentication\",\n required=True,\n ),\n StrInput(\n name=\"url\",\n display_name=\"URL\",\n info=\"URL of the image or video to analyze\",\n required=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"NSFW Analysis\", name=\"nsfw_result\", method=\"detect_nsfw\"),\n ]\n\n def detect_nsfw(self) -> Data:\n try:\n from jigsawstack import JigsawStack, JigsawStackError\n except ImportError as e:\n jigsawstack_import_error = (\n \"JigsawStack package not found. Please install it using: pip install jigsawstack>=0.2.7\"\n )\n raise ImportError(jigsawstack_import_error) from e\n\n try:\n client = JigsawStack(api_key=self.api_key)\n\n # Build request parameters\n params = {\"url\": self.url}\n\n response = client.validate.nsfw(params)\n\n api_error_msg = \"JigsawStack API returned unsuccessful response\"\n if not response.get(\"success\", False):\n raise ValueError(api_error_msg)\n\n return Data(data=response)\n\n except ValueError:\n raise\n except JigsawStackError as e:\n error_data = {\"error\": str(e), \"success\": False}\n self.status = f\"Error: {e!s}\"\n return Data(data=error_data)\n" - }, - "url": { - "_input_type": "StrInput", - "advanced": false, - "display_name": "URL", - "dynamic": false, - "info": "URL of the image or video to analyze", - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "url", - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - } - }, - "tool_mode": false - }, - "JigsawStackObjectDetection": { - "base_classes": [ - "JSON" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Perform object detection on images using JigsawStack's Object Detection Model, capable of image grounding, segmentation and computer use.", - "display_name": "Object Detection", - "documentation": "https://jigsawstack.com/docs/api-reference/ai/object-detection", - "edited": false, - "field_order": [ - "api_key", - "prompts", - "url", - "file_store_key", - "annotated_image", - "features", - "return_type" - ], - "frozen": false, - "icon": "JigsawStack", - "legacy": false, - "metadata": { - "code_hash": "fceaf0286f20", - "dependencies": { - "dependencies": [ - { - "name": "lfx", - "version": null - }, - { - "name": "jigsawstack", - "version": "0.2.7" - } - ], - "total_dependencies": 2 - }, - "module": "lfx.components.jigsawstack.object_detection.JigsawStackObjectDetectionComponent" - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Object Detection results", - "group_outputs": false, - "method": "detect_objects", - "name": "object_detection_results", - "selected": "JSON", - "tool_mode": true, - "types": [ - "JSON" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "annotated_image": { - "_input_type": "BoolInput", - "advanced": false, - "display_name": "Return Annotated Image", - "dynamic": false, - "info": "If true, will return an url for annotated image with detected objects.", - "list": false, - "list_add_label": "Add More", - "name": "annotated_image", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "bool", - "value": true - }, - "api_key": { - "_input_type": "SecretStrInput", - "advanced": false, - "display_name": "JigsawStack API Key", - "dynamic": false, - "info": "Your JigsawStack API key for authentication", - "input_types": [], - "load_from_db": true, - "name": "api_key", - "override_skip": false, - "password": true, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from lfx.custom.custom_component.component import Component\nfrom lfx.io import BoolInput, DropdownInput, MessageTextInput, Output, SecretStrInput\nfrom lfx.schema.data import Data\n\n\nclass JigsawStackObjectDetectionComponent(Component):\n display_name = \"Object Detection\"\n description = \"Perform object detection on images using JigsawStack's Object Detection Model, \\\n capable of image grounding, segmentation and computer use.\"\n documentation = \"https://jigsawstack.com/docs/api-reference/ai/object-detection\"\n icon = \"JigsawStack\"\n name = \"JigsawStackObjectDetection\"\n\n inputs = [\n SecretStrInput(\n name=\"api_key\",\n display_name=\"JigsawStack API Key\",\n info=\"Your JigsawStack API key for authentication\",\n required=True,\n ),\n MessageTextInput(\n name=\"prompts\",\n display_name=\"Prompts\",\n info=\"The prompts to ground the object detection model. \\\n You can pass a list of comma-separated prompts to extract different information from the image.\",\n required=False,\n tool_mode=True,\n ),\n MessageTextInput(\n name=\"url\",\n display_name=\"URL\",\n info=\"The image URL. Not required if file_store_key is specified.\",\n required=False,\n tool_mode=True,\n ),\n MessageTextInput(\n name=\"file_store_key\",\n display_name=\"File Store Key\",\n info=\"The key used to store the image on Jigsawstack File Storage. Not required if url is specified.\",\n required=False,\n tool_mode=True,\n ),\n BoolInput(\n name=\"annotated_image\",\n display_name=\"Return Annotated Image\",\n info=\"If true, will return an url for annotated image with detected objects.\",\n required=False,\n value=True,\n ),\n DropdownInput(\n name=\"features\",\n display_name=\"Features\",\n info=\"Select the features to enable for object detection\",\n required=False,\n options=[\"object_detection\", \"gui\"],\n value=[\"object_detection\", \"gui\"],\n ),\n DropdownInput(\n name=\"return_type\",\n display_name=\"Return Type\",\n info=\"Select the return type for the object detection results such as masks or annotations.\",\n required=False,\n options=[\"url\", \"base64\"],\n value=\"url\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Object Detection results\", name=\"object_detection_results\", method=\"detect_objects\"),\n ]\n\n def detect_objects(self) -> Data:\n try:\n from jigsawstack import JigsawStack, JigsawStackError\n except ImportError as e:\n jigsawstack_import_error = (\n \"JigsawStack package not found. Please install it using: pip install jigsawstack>=0.2.7\"\n )\n raise ImportError(jigsawstack_import_error) from e\n\n try:\n client = JigsawStack(api_key=self.api_key)\n\n # build request object\n params = {}\n if self.prompts:\n if isinstance(self.prompts, list):\n params[\"prompt\"] = self.prompts\n elif isinstance(self.prompts, str):\n if \",\" in self.prompts:\n # Split by comma and strip whitespace\n params[\"prompt\"] = [p.strip() for p in self.prompts.split(\",\")]\n else:\n params[\"prompt\"] = [self.prompts.strip()]\n else:\n invalid_prompt_error = \"Prompt must be a list of strings or a single string\"\n raise ValueError(invalid_prompt_error)\n if self.url:\n params[\"url\"] = self.url\n if self.file_store_key:\n params[\"file_store_key\"] = self.file_store_key\n\n # if both url and file_store_key are not provided, raise an error\n if not self.url and not self.file_store_key:\n missing_url_error = \"Either URL or File Store Key must be provided to perform object detection\"\n raise ValueError(missing_url_error)\n\n params[\"annotated_image\"] = self.annotated_image\n if self.features:\n params[\"features\"] = self.features\n\n # Call web scraping\n response = client.vision.object_detection(params)\n\n if not response.get(\"success\", False):\n failed_response_error = \"JigsawStack API returned unsuccessful response\"\n raise ValueError(failed_response_error)\n\n return Data(data=response)\n\n except JigsawStackError as e:\n error_data = {\"error\": str(e), \"success\": False}\n self.status = f\"Error: {e!s}\"\n return Data(data=error_data)\n" - }, - "features": { - "_input_type": "DropdownInput", - "advanced": false, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Features", - "dynamic": false, - "external_options": {}, - "info": "Select the features to enable for object detection", - "name": "features", - "options": [ - "object_detection", - "gui" - ], - "options_metadata": [], - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "toggle": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "str", - "value": [ - "object_detection", - "gui" - ] - }, - "file_store_key": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "File Store Key", - "dynamic": false, - "info": "The key used to store the image on Jigsawstack File Storage. Not required if url is specified.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "file_store_key", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "prompts": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "Prompts", - "dynamic": false, - "info": "The prompts to ground the object detection model. You can pass a list of comma-separated prompts to extract different information from the image.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "prompts", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "return_type": { - "_input_type": "DropdownInput", - "advanced": false, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Return Type", - "dynamic": false, - "external_options": {}, - "info": "Select the return type for the object detection results such as masks or annotations.", - "name": "return_type", - "options": [ - "url", - "base64" - ], - "options_metadata": [], - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "toggle": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "str", - "value": "url" - }, - "url": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "URL", - "dynamic": false, - "info": "The image URL. Not required if file_store_key is specified.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "url", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - } - }, - "tool_mode": false - }, - "JigsawStackSentiment": { - "base_classes": [ - "JSON", - "Message" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Analyze sentiment of text using JigsawStack AI", - "display_name": "Sentiment Analysis", - "documentation": "https://jigsawstack.com/docs/api-reference/ai/sentiment", - "edited": false, - "field_order": [ - "api_key", - "text" - ], - "frozen": false, - "icon": "JigsawStack", - "legacy": false, - "metadata": { - "code_hash": "00d0377e5c2e", - "dependencies": { - "dependencies": [ - { - "name": "lfx", - "version": null - }, - { - "name": "jigsawstack", - "version": "0.2.7" - } - ], - "total_dependencies": 2 - }, - "module": "lfx.components.jigsawstack.sentiment.JigsawStackSentimentComponent" - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Sentiment Data", - "group_outputs": false, - "method": "analyze_sentiment", - "name": "sentiment_data", - "selected": "JSON", - "tool_mode": true, - "types": [ - "JSON" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "Sentiment Text", - "group_outputs": false, - "method": "get_sentiment_text", - "name": "sentiment_text", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "api_key": { - "_input_type": "SecretStrInput", - "advanced": false, - "display_name": "JigsawStack API Key", - "dynamic": false, - "info": "Your JigsawStack API key for authentication", - "input_types": [], - "load_from_db": true, - "name": "api_key", - "override_skip": false, - "password": true, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from lfx.custom.custom_component.component import Component\nfrom lfx.io import MessageTextInput, Output, SecretStrInput\nfrom lfx.schema.data import Data\nfrom lfx.schema.message import Message\n\n\nclass JigsawStackSentimentComponent(Component):\n display_name = \"Sentiment Analysis\"\n description = \"Analyze sentiment of text using JigsawStack AI\"\n documentation = \"https://jigsawstack.com/docs/api-reference/ai/sentiment\"\n icon = \"JigsawStack\"\n name = \"JigsawStackSentiment\"\n\n inputs = [\n SecretStrInput(\n name=\"api_key\",\n display_name=\"JigsawStack API Key\",\n info=\"Your JigsawStack API key for authentication\",\n required=True,\n ),\n MessageTextInput(\n name=\"text\",\n display_name=\"Text\",\n info=\"Text to analyze for sentiment\",\n required=True,\n tool_mode=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Sentiment Data\", name=\"sentiment_data\", method=\"analyze_sentiment\"),\n Output(display_name=\"Sentiment Text\", name=\"sentiment_text\", method=\"get_sentiment_text\"),\n ]\n\n def analyze_sentiment(self) -> Data:\n try:\n from jigsawstack import JigsawStack, JigsawStackError\n except ImportError as e:\n jigsawstack_import_error = (\n \"JigsawStack package not found. Please install it using: pip install jigsawstack>=0.2.7\"\n )\n raise ImportError(jigsawstack_import_error) from e\n\n try:\n client = JigsawStack(api_key=self.api_key)\n response = client.sentiment({\"text\": self.text})\n\n api_error_msg = \"JigsawStack API returned unsuccessful response\"\n if not response.get(\"success\", False):\n raise ValueError(api_error_msg)\n\n sentiment_data = response.get(\"sentiment\", {})\n\n result_data = {\n \"text_analyzed\": self.text,\n \"sentiment\": sentiment_data.get(\"sentiment\", \"Unknown\"),\n \"emotion\": sentiment_data.get(\"emotion\", \"Unknown\"),\n \"score\": sentiment_data.get(\"score\", 0.0),\n \"sentences\": response.get(\"sentences\", []),\n \"success\": True,\n }\n\n self.status = (\n f\"Sentiment: {sentiment_data.get('sentiment', 'Unknown')} | \"\n f\"Emotion: {sentiment_data.get('emotion', 'Unknown')} | \"\n f\"Score: {sentiment_data.get('score', 0.0):.3f}\"\n )\n\n return Data(data=result_data)\n\n except JigsawStackError as e:\n error_data = {\"error\": str(e), \"text_analyzed\": self.text, \"success\": False}\n self.status = f\"Error: {e!s}\"\n return Data(data=error_data)\n\n def get_sentiment_text(self) -> Message:\n try:\n from jigsawstack import JigsawStack, JigsawStackError\n except ImportError:\n return Message(text=\"Error: JigsawStack package not found. Please install it with: pip install jigsawstack\")\n\n try:\n client = JigsawStack(api_key=self.api_key)\n response = client.sentiment({\"text\": self.text})\n\n sentiment_data = response.get(\"sentiment\", {})\n sentences = response.get(\"sentences\", [])\n\n # Format the output\n formatted_output = f\"\"\"Sentiment Analysis Results:\n\nText: {self.text}\n\nOverall Sentiment: {sentiment_data.get(\"sentiment\", \"Unknown\")}\nEmotion: {sentiment_data.get(\"emotion\", \"Unknown\")}\nScore: {sentiment_data.get(\"score\", 0.0):.3f}\n\nSentence-by-sentence Analysis:\n\"\"\"\n\n for i, sentence in enumerate(sentences, 1):\n formatted_output += (\n f\"{i}. {sentence.get('text', '')}\\n\"\n f\" Sentiment: {sentence.get('sentiment', 'Unknown')} | \"\n f\"Emotion: {sentence.get('emotion', 'Unknown')} | \"\n f\"Score: {sentence.get('score', 0.0):.3f}\\n\"\n )\n\n return Message(text=formatted_output)\n\n except JigsawStackError as e:\n return Message(text=f\"Error analyzing sentiment: {e!s}\")\n" - }, - "text": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "Text", - "dynamic": false, - "info": "Text to analyze for sentiment", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "text", - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - } - }, - "tool_mode": false - }, - "JigsawStackTextToSQL": { - "base_classes": [ - "JSON" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Convert natural language to SQL queries using JigsawStack AI", - "display_name": "Text to SQL", - "documentation": "https://jigsawstack.com/docs/api-reference/ai/text-to-sql", - "edited": false, - "field_order": [ - "api_key", - "prompt", - "sql_schema", - "file_store_key" - ], - "frozen": false, - "icon": "JigsawStack", - "legacy": false, - "metadata": { - "code_hash": "30aa0cc15fb8", - "dependencies": { - "dependencies": [ - { - "name": "lfx", - "version": null - }, - { - "name": "jigsawstack", - "version": "0.2.7" - } - ], - "total_dependencies": 2 - }, - "module": "lfx.components.jigsawstack.text_to_sql.JigsawStackTextToSQLComponent" - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "SQL Query", - "group_outputs": false, - "method": "generate_sql", - "name": "sql_query", - "selected": "JSON", - "tool_mode": true, - "types": [ - "JSON" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "api_key": { - "_input_type": "SecretStrInput", - "advanced": false, - "display_name": "JigsawStack API Key", - "dynamic": false, - "info": "Your JigsawStack API key for authentication", - "input_types": [], - "load_from_db": true, - "name": "api_key", - "override_skip": false, - "password": true, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from lfx.custom.custom_component.component import Component\nfrom lfx.io import MessageTextInput, Output, QueryInput, SecretStrInput, StrInput\nfrom lfx.schema.data import Data\n\n\nclass JigsawStackTextToSQLComponent(Component):\n display_name = \"Text to SQL\"\n description = \"Convert natural language to SQL queries using JigsawStack AI\"\n documentation = \"https://jigsawstack.com/docs/api-reference/ai/text-to-sql\"\n icon = \"JigsawStack\"\n name = \"JigsawStackTextToSQL\"\n\n inputs = [\n SecretStrInput(\n name=\"api_key\",\n display_name=\"JigsawStack API Key\",\n info=\"Your JigsawStack API key for authentication\",\n required=True,\n ),\n QueryInput(\n name=\"prompt\",\n display_name=\"Prompt\",\n info=\"Natural language description of the SQL query you want to generate\",\n required=True,\n tool_mode=True,\n ),\n MessageTextInput(\n name=\"sql_schema\",\n display_name=\"SQL Schema\",\n info=(\n \"The database schema information. Can be a CREATE TABLE statement or schema description. \"\n \"Specifying this parameter improves SQL generation accuracy by applying \"\n \"database-specific syntax and optimizations.\"\n ),\n required=False,\n tool_mode=True,\n ),\n StrInput(\n name=\"file_store_key\",\n display_name=\"File Store Key\",\n info=(\n \"The key used to store the database schema on Jigsawstack file Storage. \"\n \"Not required if sql_schema is specified.\"\n ),\n required=False,\n tool_mode=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"SQL Query\", name=\"sql_query\", method=\"generate_sql\"),\n ]\n\n def generate_sql(self) -> Data:\n try:\n from jigsawstack import JigsawStack, JigsawStackError\n except ImportError as e:\n jigsawstack_import_error = (\n \"JigsawStack package not found. Please install it using: pip install jigsawstack>=0.2.7\"\n )\n raise ImportError(jigsawstack_import_error) from e\n\n try:\n schema_error = \"Either 'sql_schema' or 'file_store_key' must be provided\"\n if not self.sql_schema and not self.file_store_key:\n raise ValueError(schema_error)\n\n # build request object\n params = {\"prompt\": self.prompt}\n\n if self.sql_schema:\n params[\"sql_schema\"] = self.sql_schema\n if self.file_store_key:\n params[\"file_store_key\"] = self.file_store_key\n\n client = JigsawStack(api_key=self.api_key)\n response = client.text_to_sql(params)\n\n api_error_msg = \"JigsawStack API returned unsuccessful response\"\n if not response.get(\"success\", False):\n raise ValueError(api_error_msg)\n\n return Data(data=response)\n\n except ValueError:\n raise\n except JigsawStackError as e:\n error_data = {\"error\": str(e), \"success\": False}\n self.status = f\"Error: {e!s}\"\n return Data(data=error_data)\n" - }, - "file_store_key": { - "_input_type": "StrInput", - "advanced": false, - "display_name": "File Store Key", - "dynamic": false, - "info": "The key used to store the database schema on Jigsawstack file Storage. Not required if sql_schema is specified.", - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "file_store_key", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "prompt": { - "_input_type": "QueryInput", - "advanced": false, - "display_name": "Prompt", - "dynamic": false, - "info": "Natural language description of the SQL query you want to generate", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "prompt", - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "query", - "value": "" - }, - "sql_schema": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "SQL Schema", - "dynamic": false, - "info": "The database schema information. Can be a CREATE TABLE statement or schema description. Specifying this parameter improves SQL generation accuracy by applying database-specific syntax and optimizations.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "sql_schema", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - } - }, - "tool_mode": false - }, - "JigsawStackTextTranslate": { - "base_classes": [ - "JSON" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Translate text from one language to another with support for multiple text formats.", - "display_name": "Text Translate", - "documentation": "https://jigsawstack.com/docs/api-reference/ai/translate", - "edited": false, - "field_order": [ - "api_key", - "target_language", - "text" - ], - "frozen": false, - "icon": "JigsawStack", - "legacy": false, - "metadata": { - "code_hash": "46233e19961f", - "dependencies": { - "dependencies": [ - { - "name": "lfx", - "version": null - }, - { - "name": "jigsawstack", - "version": "0.2.7" - } - ], - "total_dependencies": 2 - }, - "module": "lfx.components.jigsawstack.text_translate.JigsawStackTextTranslateComponent" - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Translation Results", - "group_outputs": false, - "method": "translation", - "name": "translation_results", - "selected": "JSON", - "tool_mode": true, - "types": [ - "JSON" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "api_key": { - "_input_type": "SecretStrInput", - "advanced": false, - "display_name": "JigsawStack API Key", - "dynamic": false, - "info": "Your JigsawStack API key for authentication", - "input_types": [], - "load_from_db": true, - "name": "api_key", - "override_skip": false, - "password": true, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from lfx.custom.custom_component.component import Component\nfrom lfx.io import MessageTextInput, Output, SecretStrInput, StrInput\nfrom lfx.schema.data import Data\n\n\nclass JigsawStackTextTranslateComponent(Component):\n display_name = \"Text Translate\"\n description = \"Translate text from one language to another with support for multiple text formats.\"\n documentation = \"https://jigsawstack.com/docs/api-reference/ai/translate\"\n icon = \"JigsawStack\"\n name = \"JigsawStackTextTranslate\"\n inputs = [\n SecretStrInput(\n name=\"api_key\",\n display_name=\"JigsawStack API Key\",\n info=\"Your JigsawStack API key for authentication\",\n required=True,\n ),\n StrInput(\n name=\"target_language\",\n display_name=\"Target Language\",\n info=\"The language code of the target language to translate to. \\\n Language code is identified by a unique ISO 639-1 two-letter code\",\n required=True,\n tool_mode=True,\n ),\n MessageTextInput(\n name=\"text\",\n display_name=\"Text\",\n info=\"The text to translate. This can be a single string or a list of strings. \\\n If a list is provided, each string will be translated separately.\",\n required=True,\n is_list=True,\n tool_mode=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Translation Results\", name=\"translation_results\", method=\"translation\"),\n ]\n\n def translation(self) -> Data:\n try:\n from jigsawstack import JigsawStack, JigsawStackError\n except ImportError as e:\n jigsawstack_import_error = (\n \"JigsawStack package not found. Please install it using: pip install jigsawstack>=0.2.7\"\n )\n raise ImportError(jigsawstack_import_error) from e\n\n try:\n client = JigsawStack(api_key=self.api_key)\n\n # build request object\n params = {}\n if self.target_language:\n params[\"target_language\"] = self.target_language\n\n if self.text:\n if isinstance(self.text, list):\n params[\"text\"] = self.text\n else:\n params[\"text\"] = [self.text]\n\n # Call web scraping\n response = client.translate.text(params)\n\n if not response.get(\"success\", False):\n failed_response_error = \"JigsawStack API returned unsuccessful response\"\n raise ValueError(failed_response_error)\n\n return Data(data=response)\n\n except JigsawStackError as e:\n error_data = {\"error\": str(e), \"success\": False}\n self.status = f\"Error: {e!s}\"\n return Data(data=error_data)\n" - }, - "target_language": { - "_input_type": "StrInput", - "advanced": false, - "display_name": "Target Language", - "dynamic": false, - "info": "The language code of the target language to translate to. Language code is identified by a unique ISO 639-1 two-letter code", - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "target_language", - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "text": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "Text", - "dynamic": false, - "info": "The text to translate. This can be a single string or a list of strings. If a list is provided, each string will be translated separately.", - "input_types": [ - "Message" - ], - "list": true, - "list_add_label": "Add More", - "load_from_db": false, - "name": "text", - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - } - }, - "tool_mode": false - }, - "JigsawStackVOCR": { - "base_classes": [ - "JSON" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Extract data from any document type in a consistent structure with fine-tuned vLLMs for the highest accuracy", - "display_name": "VOCR", - "documentation": "https://jigsawstack.com/docs/api-reference/ai/vocr", - "edited": false, - "field_order": [ - "api_key", - "prompts", - "url", - "file_store_key", - "page_range_start", - "page_range_end" - ], - "frozen": false, - "icon": "JigsawStack", - "legacy": false, - "metadata": { - "code_hash": "a44491947ba7", - "dependencies": { - "dependencies": [ - { - "name": "lfx", - "version": null - }, - { - "name": "jigsawstack", - "version": "0.2.7" - } - ], - "total_dependencies": 2 - }, - "module": "lfx.components.jigsawstack.vocr.JigsawStackVOCRComponent" - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "VOCR results", - "group_outputs": false, - "method": "vocr", - "name": "vocr_results", - "selected": "JSON", - "tool_mode": true, - "types": [ - "JSON" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "api_key": { - "_input_type": "SecretStrInput", - "advanced": false, - "display_name": "JigsawStack API Key", - "dynamic": false, - "info": "Your JigsawStack API key for authentication", - "input_types": [], - "load_from_db": true, - "name": "api_key", - "override_skip": false, - "password": true, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from lfx.custom.custom_component.component import Component\nfrom lfx.io import IntInput, MessageTextInput, Output, SecretStrInput, StrInput\nfrom lfx.schema.data import Data\n\n\nclass JigsawStackVOCRComponent(Component):\n display_name = \"VOCR\"\n description = \"Extract data from any document type in a consistent structure with fine-tuned \\\n vLLMs for the highest accuracy\"\n documentation = \"https://jigsawstack.com/docs/api-reference/ai/vocr\"\n icon = \"JigsawStack\"\n name = \"JigsawStackVOCR\"\n\n inputs = [\n SecretStrInput(\n name=\"api_key\",\n display_name=\"JigsawStack API Key\",\n info=\"Your JigsawStack API key for authentication\",\n required=True,\n ),\n MessageTextInput(\n name=\"prompts\",\n display_name=\"Prompts\",\n info=\"The prompts used to describe the image. Default prompt is Describe the image in detail. \\\n You can pass a list of comma-separated prompts to extract different information from the image.\",\n required=False,\n tool_mode=True,\n ),\n StrInput(\n name=\"url\",\n display_name=\"URL\",\n info=\"The image or document url. Not required if file_store_key is specified.\",\n required=False,\n tool_mode=True,\n ),\n StrInput(\n name=\"file_store_key\",\n display_name=\"File Store Key\",\n info=\"The key used to store the image on Jigsawstack File Storage. Not required if url is specified.\",\n required=False,\n tool_mode=True,\n ),\n IntInput(\n name=\"page_range_start\",\n display_name=\"Page Range\",\n info=\"Page range start limit for the document. If not specified, all pages will be processed.\",\n required=False,\n ),\n IntInput(\n name=\"page_range_end\",\n display_name=\"Page Range End\",\n info=\"Page range end limit for the document. If not specified, all pages will be processed.\",\n required=False,\n ),\n ]\n\n outputs = [\n Output(display_name=\"VOCR results\", name=\"vocr_results\", method=\"vocr\"),\n ]\n\n def vocr(self) -> Data:\n try:\n from jigsawstack import JigsawStack, JigsawStackError\n except ImportError as e:\n jigsawstack_import_error = (\n \"JigsawStack package not found. Please install it using: pip install jigsawstack>=0.2.7\"\n )\n raise ImportError(jigsawstack_import_error) from e\n\n try:\n client = JigsawStack(api_key=self.api_key)\n\n # build request object\n params = {}\n if self.prompts:\n if isinstance(self.prompts, list):\n params[\"prompt\"] = self.prompts\n elif isinstance(self.prompts, str):\n if \",\" in self.prompts:\n # Split by comma and strip whitespace\n params[\"prompt\"] = [p.strip() for p in self.prompts.split(\",\")]\n else:\n params[\"prompt\"] = [self.prompts.strip()]\n else:\n invalid_prompt_error = \"Prompt must be a list of strings or a single string\"\n raise ValueError(invalid_prompt_error)\n if self.url:\n params[\"url\"] = self.url\n if self.file_store_key:\n params[\"file_store_key\"] = self.file_store_key\n\n if self.page_range_start and self.page_range_end:\n params[\"page_range\"] = [self.page_range_start, self.page_range_end]\n\n # Call VOCR\n response = client.vision.vocr(params)\n\n if not response.get(\"success\", False):\n failed_response_error = \"JigsawStack API returned unsuccessful response\"\n raise ValueError(failed_response_error)\n\n return Data(data=response)\n\n except JigsawStackError as e:\n error_data = {\"error\": str(e), \"success\": False}\n self.status = f\"Error: {e!s}\"\n return Data(data=error_data)\n" - }, - "file_store_key": { - "_input_type": "StrInput", - "advanced": false, - "display_name": "File Store Key", - "dynamic": false, - "info": "The key used to store the image on Jigsawstack File Storage. Not required if url is specified.", - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "file_store_key", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "page_range_end": { - "_input_type": "IntInput", - "advanced": false, - "display_name": "Page Range End", - "dynamic": false, - "info": "Page range end limit for the document. If not specified, all pages will be processed.", - "list": false, - "list_add_label": "Add More", - "name": "page_range_end", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "int", - "value": 0 - }, - "page_range_start": { - "_input_type": "IntInput", - "advanced": false, - "display_name": "Page Range", - "dynamic": false, - "info": "Page range start limit for the document. If not specified, all pages will be processed.", - "list": false, - "list_add_label": "Add More", - "name": "page_range_start", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "int", - "value": 0 - }, - "prompts": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "Prompts", - "dynamic": false, - "info": "The prompts used to describe the image. Default prompt is Describe the image in detail. You can pass a list of comma-separated prompts to extract different information from the image.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "prompts", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "url": { - "_input_type": "StrInput", - "advanced": false, - "display_name": "URL", - "dynamic": false, - "info": "The image or document url. Not required if file_store_key is specified.", - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "url", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - } - }, - "tool_mode": false - } - } - ], [ "langchain_utilities", { @@ -85914,1217 +82892,6 @@ } } ], - [ - "twelvelabs", - { - "ConvertAstraToTwelveLabs": { - "base_classes": [ - "Message" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Converts Astra DB search results to inputs compatible with TwelveLabs Pegasus.", - "display_name": "Convert Astra DB to Pegasus Input", - "documentation": "https://github.com/twelvelabs-io/twelvelabs-developer-experience/blob/main/integrations/Langflow/TWELVE_LABS_COMPONENTS_README.md", - "edited": false, - "field_order": [ - "astra_results" - ], - "frozen": false, - "icon": "TwelveLabs", - "legacy": false, - "metadata": { - "code_hash": "2a65cbf14ce5", - "dependencies": { - "dependencies": [ - { - "name": "lfx", - "version": null - } - ], - "total_dependencies": 1 - }, - "module": "lfx.components.twelvelabs.convert_astra_results.ConvertAstraToTwelveLabs" - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Index ID", - "group_outputs": false, - "method": "get_index_id", - "name": "index_id", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "Video ID", - "group_outputs": false, - "method": "get_video_id", - "name": "video_id", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "astra_results": { - "_input_type": "HandleInput", - "advanced": false, - "display_name": "Astra DB Results", - "dynamic": false, - "info": "Search results from Astra DB component", - "input_types": [ - "Data", - "JSON" - ], - "list": true, - "list_add_label": "Add More", - "name": "astra_results", - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "other", - "value": "" - }, - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from typing import Any\n\nfrom lfx.custom import Component\nfrom lfx.io import HandleInput, Output\nfrom lfx.schema import Data\nfrom lfx.schema.message import Message\n\n\nclass ConvertAstraToTwelveLabs(Component):\n \"\"\"Convert Astra DB search results to TwelveLabs Pegasus inputs.\"\"\"\n\n display_name = \"Convert Astra DB to Pegasus Input\"\n description = \"Converts Astra DB search results to inputs compatible with TwelveLabs Pegasus.\"\n icon = \"TwelveLabs\"\n name = \"ConvertAstraToTwelveLabs\"\n documentation = \"https://github.com/twelvelabs-io/twelvelabs-developer-experience/blob/main/integrations/Langflow/TWELVE_LABS_COMPONENTS_README.md\"\n\n inputs = [\n HandleInput(\n name=\"astra_results\",\n display_name=\"Astra DB Results\",\n input_types=[\"Data\", \"JSON\"],\n info=\"Search results from Astra DB component\",\n required=True,\n is_list=True,\n )\n ]\n\n outputs = [\n Output(\n name=\"index_id\",\n display_name=\"Index ID\",\n type_=Message,\n method=\"get_index_id\",\n ),\n Output(\n name=\"video_id\",\n display_name=\"Video ID\",\n type_=Message,\n method=\"get_video_id\",\n ),\n ]\n\n def __init__(self, **kwargs):\n super().__init__(**kwargs)\n self._video_id = None\n self._index_id = None\n\n def build(self, **kwargs: Any) -> None: # noqa: ARG002 - Required for parent class compatibility\n \"\"\"Process the Astra DB results and extract TwelveLabs index information.\"\"\"\n if not self.astra_results:\n return\n\n # Convert to list if single item\n results = self.astra_results if isinstance(self.astra_results, list) else [self.astra_results]\n\n # Try to extract index information from metadata\n for doc in results:\n if not isinstance(doc, Data):\n continue\n\n # Get the metadata, handling the nested structure\n metadata = {}\n if hasattr(doc, \"metadata\") and isinstance(doc.metadata, dict):\n # Handle nested metadata using .get() method\n metadata = doc.metadata.get(\"metadata\", doc.metadata)\n\n # Extract index_id and video_id\n self._index_id = metadata.get(\"index_id\")\n self._video_id = metadata.get(\"video_id\")\n\n # If we found both, we can stop searching\n if self._index_id and self._video_id:\n break\n\n def get_video_id(self) -> Message:\n \"\"\"Return the extracted video ID as a Message.\"\"\"\n self.build()\n return Message(text=self._video_id if self._video_id else \"\")\n\n def get_index_id(self) -> Message:\n \"\"\"Return the extracted index ID as a Message.\"\"\"\n self.build()\n return Message(text=self._index_id if self._index_id else \"\")\n" - } - }, - "tool_mode": false - }, - "SplitVideo": { - "base_classes": [ - "JSON" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Split a video into multiple clips of specified duration.", - "display_name": "Split Video", - "documentation": "https://github.com/twelvelabs-io/twelvelabs-developer-experience/blob/main/integrations/Langflow/TWELVE_LABS_COMPONENTS_README.md", - "edited": false, - "field_order": [ - "videodata", - "clip_duration", - "last_clip_handling", - "include_original" - ], - "frozen": false, - "icon": "TwelveLabs", - "legacy": false, - "metadata": { - "code_hash": "56ccb4106c30", - "dependencies": { - "dependencies": [ - { - "name": "lfx", - "version": null - } - ], - "total_dependencies": 1 - }, - "module": "lfx.components.twelvelabs.split_video.SplitVideoComponent" - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Video Clips", - "group_outputs": false, - "method": "process", - "name": "clips", - "selected": "JSON", - "tool_mode": true, - "types": [ - "JSON" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "clip_duration": { - "_input_type": "IntInput", - "advanced": false, - "display_name": "Clip Duration (seconds)", - "dynamic": false, - "info": "Duration of each clip in seconds", - "list": false, - "list_add_label": "Add More", - "name": "clip_duration", - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "int", - "value": 30 - }, - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "import hashlib\nimport math\nimport subprocess\nfrom datetime import datetime, timezone\nfrom pathlib import Path\nfrom typing import Any\n\nfrom lfx.custom import Component\nfrom lfx.inputs import BoolInput, DropdownInput, HandleInput, IntInput\nfrom lfx.schema import Data\nfrom lfx.template import Output\nfrom lfx.utils.validate_cloud import raise_error_if_astra_cloud_disable_component\n\ndisable_component_in_astra_cloud_msg = (\n \"Video processing is not supported in Astra cloud environment. \"\n \"Video components require local file system access for processing. \"\n \"Please use local storage mode or process videos locally before uploading.\"\n)\n\n\nclass SplitVideoComponent(Component):\n \"\"\"A component that splits a video into multiple clips of specified duration using FFmpeg.\"\"\"\n\n display_name = \"Split Video\"\n description = \"Split a video into multiple clips of specified duration.\"\n icon = \"TwelveLabs\"\n name = \"SplitVideo\"\n documentation = \"https://github.com/twelvelabs-io/twelvelabs-developer-experience/blob/main/integrations/Langflow/TWELVE_LABS_COMPONENTS_README.md\"\n\n inputs = [\n HandleInput(\n name=\"videodata\",\n display_name=\"Video Data\",\n info=\"Input video data from VideoFile component\",\n required=True,\n input_types=[\"Data\", \"JSON\"],\n ),\n IntInput(\n name=\"clip_duration\",\n display_name=\"Clip Duration (seconds)\",\n info=\"Duration of each clip in seconds\",\n required=True,\n value=30,\n ),\n DropdownInput(\n name=\"last_clip_handling\",\n display_name=\"Last Clip Handling\",\n info=(\n \"How to handle the final clip when it would be shorter than the specified duration:\\n\"\n \"- Truncate: Skip the final clip entirely if it's shorter than the specified duration\\n\"\n \"- Overlap Previous: Start the final clip earlier to maintain full duration, \"\n \"overlapping with previous clip\\n\"\n \"- Keep Short: Keep the final clip at its natural length, even if shorter than specified duration\"\n ),\n options=[\"Truncate\", \"Overlap Previous\", \"Keep Short\"],\n value=\"Overlap Previous\",\n required=True,\n ),\n BoolInput(\n name=\"include_original\",\n display_name=\"Include Original Video\",\n info=\"Whether to include the original video in the output\",\n value=False,\n ),\n ]\n\n outputs = [\n Output(\n name=\"clips\",\n display_name=\"Video Clips\",\n method=\"process\",\n output_types=[\"JSON\"],\n ),\n ]\n\n def get_video_duration(self, video_path: str) -> float:\n \"\"\"Get video duration using FFmpeg.\"\"\"\n try:\n # Validate video path to prevent shell injection\n if not isinstance(video_path, str) or any(c in video_path for c in \";&|`$(){}[]<>*?!#~\"):\n error_msg = \"Invalid video path\"\n raise ValueError(error_msg)\n\n cmd = [\n \"ffprobe\",\n \"-v\",\n \"error\",\n \"-show_entries\",\n \"format=duration\",\n \"-of\",\n \"default=noprint_wrappers=1:nokey=1\",\n video_path,\n ]\n result = subprocess.run( # noqa: S603\n cmd,\n capture_output=True,\n text=True,\n check=False,\n shell=False, # Explicitly set shell=False for security\n )\n if result.returncode != 0:\n error_msg = f\"FFprobe error: {result.stderr}\"\n raise RuntimeError(error_msg)\n return float(result.stdout.strip())\n except Exception as e:\n self.log(f\"Error getting video duration: {e!s}\", \"ERROR\")\n raise\n\n def get_output_dir(self, video_path: str) -> str:\n \"\"\"Create a unique output directory for clips based on video name and timestamp.\"\"\"\n # Get the video filename without extension\n path_obj = Path(video_path)\n base_name = path_obj.stem\n\n # Create a timestamp\n timestamp = datetime.now(tz=timezone.utc).strftime(\"%Y-%m-%d_%H-%M-%S\")\n\n # Create a unique hash from the video path\n path_hash = hashlib.sha256(video_path.encode()).hexdigest()[:8]\n\n # Create the output directory path\n output_dir = Path(path_obj.parent) / f\"clips_{base_name}_{timestamp}_{path_hash}\"\n\n # Create the directory if it doesn't exist\n output_dir.mkdir(parents=True, exist_ok=True)\n\n return str(output_dir)\n\n def process_video(self, video_path: str, clip_duration: int, *, include_original: bool) -> list[Data]:\n \"\"\"Process video and split it into clips using FFmpeg.\"\"\"\n try:\n # Get video duration\n total_duration = self.get_video_duration(video_path)\n\n # Calculate number of clips (ceiling to include partial clip)\n num_clips = math.ceil(total_duration / clip_duration)\n self.log(\n f\"Total duration: {total_duration}s, Clip duration: {clip_duration}s, Number of clips: {num_clips}\"\n )\n\n # Create output directory for clips\n output_dir = self.get_output_dir(video_path)\n\n # Get original video info\n path_obj = Path(video_path)\n original_filename = path_obj.name\n original_name = path_obj.stem\n\n # List to store all video paths (including original if requested)\n video_paths: list[Data] = []\n\n # Add original video if requested\n if include_original:\n original_data: dict[str, Any] = {\n \"text\": video_path,\n \"metadata\": {\n \"source\": video_path,\n \"type\": \"video\",\n \"clip_index\": -1, # -1 indicates original video\n \"duration\": int(total_duration), # Convert to int\n \"original_video\": {\n \"name\": original_name,\n \"filename\": original_filename,\n \"path\": video_path,\n \"duration\": int(total_duration), # Convert to int\n \"total_clips\": int(num_clips),\n \"clip_duration\": int(clip_duration),\n },\n },\n }\n video_paths.append(Data(data=original_data))\n\n # Split video into clips\n for i in range(int(num_clips)): # Convert num_clips to int for range\n start_time = float(i * clip_duration) # Convert to float for time calculations\n end_time = min(float((i + 1) * clip_duration), total_duration)\n duration = end_time - start_time\n\n # Handle last clip if it's shorter\n if i == int(num_clips) - 1 and duration < clip_duration: # Convert num_clips to int for comparison\n if self.last_clip_handling == \"Truncate\":\n # Skip if the last clip would be too short\n continue\n if self.last_clip_handling == \"Overlap Previous\" and i > 0:\n # Start from earlier to make full duration\n start_time = total_duration - clip_duration\n duration = clip_duration\n # For \"Keep Short\", we use the original start_time and duration\n\n # Skip if duration is too small (less than 1 second)\n if duration < 1:\n continue\n\n # Generate output path\n output_path = Path(output_dir) / f\"clip_{i:03d}.mp4\"\n output_path_str = str(output_path)\n\n try:\n # Use FFmpeg to split the video\n cmd = [\n \"ffmpeg\",\n \"-i\",\n video_path,\n \"-ss\",\n str(start_time),\n \"-t\",\n str(duration),\n \"-c:v\",\n \"libx264\",\n \"-c:a\",\n \"aac\",\n \"-y\", # Overwrite output file if it exists\n output_path_str,\n ]\n\n result = subprocess.run( # noqa: S603\n cmd,\n capture_output=True,\n text=True,\n check=False,\n shell=False, # Explicitly set shell=False for security\n )\n if result.returncode != 0:\n error_msg = f\"FFmpeg error: {result.stderr}\"\n raise RuntimeError(error_msg)\n\n # Create timestamp string for metadata\n start_min = int(start_time // 60)\n start_sec = int(start_time % 60)\n end_min = int(end_time // 60)\n end_sec = int(end_time % 60)\n timestamp_str = f\"{start_min:02d}:{start_sec:02d} - {end_min:02d}:{end_sec:02d}\"\n\n # Create Data object for the clip\n clip_data: dict[str, Any] = {\n \"text\": output_path_str,\n \"metadata\": {\n \"source\": video_path,\n \"type\": \"video\",\n \"clip_index\": i,\n \"start_time\": float(start_time),\n \"end_time\": float(end_time),\n \"duration\": float(duration),\n \"original_video\": {\n \"name\": original_name,\n \"filename\": original_filename,\n \"path\": video_path,\n \"duration\": int(total_duration),\n \"total_clips\": int(num_clips),\n \"clip_duration\": int(clip_duration),\n },\n \"clip\": {\n \"index\": i,\n \"total\": int(num_clips),\n \"duration\": float(duration),\n \"start_time\": float(start_time),\n \"end_time\": float(end_time),\n \"timestamp\": timestamp_str,\n },\n },\n }\n video_paths.append(Data(data=clip_data))\n\n except Exception as e:\n self.log(f\"Error processing clip {i}: {e!s}\", \"ERROR\")\n raise\n\n self.log(f\"Created {len(video_paths)} clips in {output_dir}\")\n except Exception as e:\n self.log(f\"Error processing video: {e!s}\", \"ERROR\")\n raise\n else:\n return video_paths\n\n def process(self) -> list[Data]:\n \"\"\"Process the input video and return a list of Data objects containing the clips.\"\"\"\n # Check if we're in Astra cloud environment and raise an error if we are.\n raise_error_if_astra_cloud_disable_component(disable_component_in_astra_cloud_msg)\n\n try:\n # Get the input video path from the previous component\n if not hasattr(self, \"videodata\") or not isinstance(self.videodata, list) or len(self.videodata) != 1:\n error_msg = \"Please provide exactly one video\"\n raise ValueError(error_msg)\n\n video_path = self.videodata[0].data.get(\"text\")\n if not video_path or not Path(video_path).exists():\n error_msg = \"Invalid video path\"\n raise ValueError(error_msg)\n\n # Validate video path to prevent shell injection\n if not isinstance(video_path, str) or any(c in video_path for c in \";&|`$(){}[]<>*?!#~\"):\n error_msg = \"Invalid video path contains unsafe characters\"\n raise ValueError(error_msg)\n\n # Process the video\n return self.process_video(video_path, self.clip_duration, include_original=self.include_original)\n\n except Exception as e:\n self.log(f\"Error in split video component: {e!s}\", \"ERROR\")\n raise\n" - }, - "include_original": { - "_input_type": "BoolInput", - "advanced": false, - "display_name": "Include Original Video", - "dynamic": false, - "info": "Whether to include the original video in the output", - "list": false, - "list_add_label": "Add More", - "name": "include_original", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "bool", - "value": false - }, - "last_clip_handling": { - "_input_type": "DropdownInput", - "advanced": false, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Last Clip Handling", - "dynamic": false, - "external_options": {}, - "info": "How to handle the final clip when it would be shorter than the specified duration:\n- Truncate: Skip the final clip entirely if it's shorter than the specified duration\n- Overlap Previous: Start the final clip earlier to maintain full duration, overlapping with previous clip\n- Keep Short: Keep the final clip at its natural length, even if shorter than specified duration", - "name": "last_clip_handling", - "options": [ - "Truncate", - "Overlap Previous", - "Keep Short" - ], - "options_metadata": [], - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "toggle": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "str", - "value": "Overlap Previous" - }, - "videodata": { - "_input_type": "HandleInput", - "advanced": false, - "display_name": "Video Data", - "dynamic": false, - "info": "Input video data from VideoFile component", - "input_types": [ - "Data", - "JSON" - ], - "list": false, - "list_add_label": "Add More", - "name": "videodata", - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "other", - "value": "" - } - }, - "tool_mode": false - }, - "TwelveLabsPegasus": { - "base_classes": [ - "Message" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Chat with videos using TwelveLabs Pegasus API.", - "display_name": "TwelveLabs Pegasus", - "documentation": "https://github.com/twelvelabs-io/twelvelabs-developer-experience/blob/main/integrations/Langflow/TWELVE_LABS_COMPONENTS_README.md", - "edited": false, - "field_order": [ - "videodata", - "api_key", - "video_id", - "index_name", - "index_id", - "model_name", - "message", - "temperature" - ], - "frozen": false, - "icon": "TwelveLabs", - "legacy": false, - "metadata": { - "code_hash": "92cc032822a6", - "dependencies": { - "dependencies": [ - { - "name": "tenacity", - "version": "8.5.0" - }, - { - "name": "twelvelabs", - "version": "0.4.11" - }, - { - "name": "lfx", - "version": null - } - ], - "total_dependencies": 3 - }, - "module": "lfx.components.twelvelabs.twelvelabs_pegasus.TwelveLabsPegasus" - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Message", - "group_outputs": false, - "method": "process_video", - "name": "response", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "Video ID", - "group_outputs": false, - "method": "get_video_id", - "name": "processed_video_id", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "api_key": { - "_input_type": "SecretStrInput", - "advanced": false, - "display_name": "TwelveLabs API Key", - "dynamic": false, - "info": "Enter your TwelveLabs API Key.", - "input_types": [], - "load_from_db": true, - "name": "api_key", - "override_skip": false, - "password": true, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "import json\nimport subprocess\nimport time\nfrom pathlib import Path\nfrom typing import Any\n\nfrom tenacity import retry, stop_after_attempt, wait_exponential\nfrom twelvelabs import TwelveLabs\n\nfrom lfx.custom import Component\nfrom lfx.field_typing.range_spec import RangeSpec\nfrom lfx.inputs import DataInput, DropdownInput, MessageInput, MultilineInput, SecretStrInput, SliderInput\nfrom lfx.io import Output\nfrom lfx.schema.message import Message\n\n\nclass TaskError(Exception):\n \"\"\"Error raised when a task fails.\"\"\"\n\n\nclass TaskTimeoutError(Exception):\n \"\"\"Error raised when a task times out.\"\"\"\n\n\nclass IndexCreationError(Exception):\n \"\"\"Error raised when there's an issue with an index.\"\"\"\n\n\nclass ApiRequestError(Exception):\n \"\"\"Error raised when an API request fails.\"\"\"\n\n\nclass VideoValidationError(Exception):\n \"\"\"Error raised when video validation fails.\"\"\"\n\n\nclass TwelveLabsPegasus(Component):\n display_name = \"TwelveLabs Pegasus\"\n description = \"Chat with videos using TwelveLabs Pegasus API.\"\n icon = \"TwelveLabs\"\n name = \"TwelveLabsPegasus\"\n documentation = \"https://github.com/twelvelabs-io/twelvelabs-developer-experience/blob/main/integrations/Langflow/TWELVE_LABS_COMPONENTS_README.md\"\n\n inputs = [\n DataInput(name=\"videodata\", display_name=\"Video Data\", info=\"Video Data\", is_list=True),\n SecretStrInput(\n name=\"api_key\", display_name=\"TwelveLabs API Key\", info=\"Enter your TwelveLabs API Key.\", required=True\n ),\n MessageInput(\n name=\"video_id\",\n display_name=\"Pegasus Video ID\",\n info=\"Enter a Video ID for a previously indexed video.\",\n ),\n MessageInput(\n name=\"index_name\",\n display_name=\"Index Name\",\n info=\"Name of the index to use. If the index doesn't exist, it will be created.\",\n required=False,\n ),\n MessageInput(\n name=\"index_id\",\n display_name=\"Index ID\",\n info=\"ID of an existing index to use. If provided, index_name will be ignored.\",\n required=False,\n ),\n DropdownInput(\n name=\"model_name\",\n display_name=\"Model\",\n info=\"Pegasus model to use for indexing\",\n options=[\"pegasus1.2\"],\n value=\"pegasus1.2\",\n advanced=False,\n ),\n MultilineInput(\n name=\"message\",\n display_name=\"Prompt\",\n info=\"Message to chat with the video.\",\n required=True,\n ),\n SliderInput(\n name=\"temperature\",\n display_name=\"Temperature\",\n value=0.7,\n range_spec=RangeSpec(min=0, max=1, step=0.01),\n info=(\n \"Controls randomness in responses. Lower values are more deterministic, \"\n \"higher values are more creative.\"\n ),\n ),\n ]\n\n outputs = [\n Output(\n display_name=\"Message\",\n name=\"response\",\n method=\"process_video\",\n type_=Message,\n ),\n Output(\n display_name=\"Video ID\",\n name=\"processed_video_id\",\n method=\"get_video_id\",\n type_=Message,\n ),\n ]\n\n def __init__(self, **kwargs) -> None:\n super().__init__(**kwargs)\n\n self._task_id: str | None = None\n self._video_id: str | None = None\n self._index_id: str | None = None\n self._index_name: str | None = None\n self._message: str | None = None\n\n def _get_or_create_index(self, client: TwelveLabs) -> tuple[str, str]:\n \"\"\"Get existing index or create new one.\n\n Returns (index_id, index_name).\n \"\"\"\n # First check if index_id is provided and valid\n if hasattr(self, \"_index_id\") and self._index_id:\n try:\n index = client.index.retrieve(id=self._index_id)\n self.log(f\"Found existing index with ID: {self._index_id}\")\n except (ValueError, KeyError) as e:\n self.log(f\"Error retrieving index with ID {self._index_id}: {e!s}\", \"WARNING\")\n else:\n return self._index_id, index.name\n\n # If index_name is provided, try to find it\n if hasattr(self, \"_index_name\") and self._index_name:\n try:\n # List all indexes and find by name\n indexes = client.index.list()\n for idx in indexes:\n if idx.name == self._index_name:\n self.log(f\"Found existing index: {self._index_name} (ID: {idx.id})\")\n return idx.id, idx.name\n\n # If we get here, index wasn't found - create it\n self.log(f\"Creating new index: {self._index_name}\")\n index = client.index.create(\n name=self._index_name,\n models=[\n {\n \"name\": self.model_name if hasattr(self, \"model_name\") else \"pegasus1.2\",\n \"options\": [\"visual\", \"audio\"],\n }\n ],\n )\n except (ValueError, KeyError) as e:\n self.log(f\"Error with index name {self._index_name}: {e!s}\", \"ERROR\")\n error_message = f\"Error with index name {self._index_name}\"\n raise IndexCreationError(error_message) from e\n else:\n return index.id, index.name\n\n # If neither is provided, create a new index with timestamp\n try:\n index_name = f\"index_{int(time.time())}\"\n self.log(f\"Creating new index: {index_name}\")\n index = client.index.create(\n name=index_name,\n models=[\n {\n \"name\": self.model_name if hasattr(self, \"model_name\") else \"pegasus1.2\",\n \"options\": [\"visual\", \"audio\"],\n }\n ],\n )\n except (ValueError, KeyError) as e:\n self.log(f\"Failed to create new index: {e!s}\", \"ERROR\")\n error_message = \"Failed to create new index\"\n raise IndexCreationError(error_message) from e\n else:\n return index.id, index.name\n\n @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10), reraise=True)\n async def _make_api_request(self, method: Any, *args: Any, **kwargs: Any) -> Any:\n \"\"\"Make API request with retry logic.\n\n Retries failed requests with exponential backoff.\n \"\"\"\n try:\n return await method(*args, **kwargs)\n except (ValueError, KeyError) as e:\n self.log(f\"API request failed: {e!s}\", \"ERROR\")\n error_message = \"API request failed\"\n raise ApiRequestError(error_message) from e\n\n def wait_for_task_completion(\n self, client: TwelveLabs, task_id: str, max_retries: int = 120, sleep_time: int = 5\n ) -> Any:\n \"\"\"Wait for task completion with timeout and improved error handling.\n\n Polls the task status until completion or timeout.\n \"\"\"\n retries = 0\n consecutive_errors = 0\n max_consecutive_errors = 3\n\n while retries < max_retries:\n try:\n self.log(f\"Checking task status (attempt {retries + 1})\")\n result = client.task.retrieve(id=task_id)\n consecutive_errors = 0 # Reset error counter on success\n\n if result.status == \"ready\":\n self.log(\"Task completed successfully!\")\n return result\n if result.status == \"failed\":\n error_msg = f\"Task failed with status: {result.status}\"\n self.log(error_msg, \"ERROR\")\n raise TaskError(error_msg)\n if result.status == \"error\":\n error_msg = f\"Task encountered an error: {getattr(result, 'error', 'Unknown error')}\"\n self.log(error_msg, \"ERROR\")\n raise TaskError(error_msg)\n\n time.sleep(sleep_time)\n retries += 1\n status_msg = f\"Processing video... {retries * sleep_time}s elapsed\"\n self.status = status_msg\n self.log(status_msg)\n\n except (ValueError, KeyError) as e:\n consecutive_errors += 1\n error_msg = f\"Error checking task status: {e!s}\"\n self.log(error_msg, \"WARNING\")\n\n if consecutive_errors >= max_consecutive_errors:\n too_many_errors = \"Too many consecutive errors\"\n raise TaskError(too_many_errors) from e\n\n time.sleep(sleep_time * 2)\n continue\n\n timeout_msg = f\"Timeout after {max_retries * sleep_time} seconds\"\n self.log(timeout_msg, \"ERROR\")\n raise TaskTimeoutError(timeout_msg)\n\n def validate_video_file(self, filepath: str) -> tuple[bool, str]:\n \"\"\"Validate video file using ffprobe.\n\n Returns (is_valid, error_message).\n \"\"\"\n # Ensure filepath is a string and doesn't contain shell metacharacters\n if not isinstance(filepath, str) or any(c in filepath for c in \";&|`$(){}[]<>*?!#~\"):\n return False, \"Invalid filepath\"\n\n try:\n cmd = [\n \"ffprobe\",\n \"-loglevel\",\n \"error\",\n \"-show_entries\",\n \"stream=codec_type,codec_name\",\n \"-of\",\n \"default=nw=1\",\n \"-print_format\",\n \"json\",\n \"-show_format\",\n filepath,\n ]\n\n # Use subprocess with a list of arguments to avoid shell injection\n # We need to skip the S603 warning here as we're taking proper precautions\n # with input validation and using shell=False\n result = subprocess.run( # noqa: S603\n cmd,\n capture_output=True,\n text=True,\n check=False,\n shell=False, # Explicitly set shell=False for security\n )\n\n if result.returncode != 0:\n return False, f\"FFprobe error: {result.stderr}\"\n\n probe_data = json.loads(result.stdout)\n\n has_video = any(stream.get(\"codec_type\") == \"video\" for stream in probe_data.get(\"streams\", []))\n\n if not has_video:\n return False, \"No video stream found in file\"\n\n self.log(f\"Video validation successful: {json.dumps(probe_data, indent=2)}\")\n except subprocess.SubprocessError as e:\n return False, f\"FFprobe process error: {e!s}\"\n except json.JSONDecodeError as e:\n return False, f\"FFprobe output parsing error: {e!s}\"\n except (ValueError, OSError) as e:\n return False, f\"Validation error: {e!s}\"\n else:\n return True, \"\"\n\n def on_task_update(self, task: Any) -> None:\n \"\"\"Callback for task status updates.\n\n Updates the component status with the current task status.\n \"\"\"\n self.status = f\"Processing video... Status: {task.status}\"\n self.log(self.status)\n\n def process_video(self) -> Message:\n \"\"\"Process video using Pegasus and generate response if message is provided.\n\n Handles video indexing and question answering using the TwelveLabs API.\n \"\"\"\n # Check and initialize inputs\n if hasattr(self, \"index_id\") and self.index_id:\n self._index_id = self.index_id.text if hasattr(self.index_id, \"text\") else self.index_id\n\n if hasattr(self, \"index_name\") and self.index_name:\n self._index_name = self.index_name.text if hasattr(self.index_name, \"text\") else self.index_name\n\n if hasattr(self, \"video_id\") and self.video_id:\n self._video_id = self.video_id.text if hasattr(self.video_id, \"text\") else self.video_id\n\n if hasattr(self, \"message\") and self.message:\n self._message = self.message.text if hasattr(self.message, \"text\") else self.message\n\n try:\n # If we have a message and already processed video, use existing video_id\n if self._message and self._video_id and self._video_id != \"\":\n self.status = f\"Have video id: {self._video_id}\"\n\n client = TwelveLabs(api_key=self.api_key)\n\n self.status = f\"Processing query (w/ video ID): {self._video_id} {self._message}\"\n self.log(self.status)\n\n response = client.generate.text(\n video_id=self._video_id,\n prompt=self._message,\n temperature=self.temperature,\n )\n return Message(text=response.data)\n\n # Otherwise process new video\n if not self.videodata or not isinstance(self.videodata, list) or len(self.videodata) != 1:\n return Message(text=\"Please provide exactly one video\")\n\n video_path = self.videodata[0].data.get(\"text\")\n if not video_path or not Path(video_path).exists():\n return Message(text=\"Invalid video path\")\n\n if not self.api_key:\n return Message(text=\"No API key provided\")\n\n client = TwelveLabs(api_key=self.api_key)\n\n # Get or create index\n try:\n index_id, index_name = self._get_or_create_index(client)\n self.status = f\"Using index: {index_name} (ID: {index_id})\"\n self.log(f\"Using index: {index_name} (ID: {index_id})\")\n self._index_id = index_id\n self._index_name = index_name\n except IndexCreationError as e:\n return Message(text=f\"Failed to get/create index: {e}\")\n\n with Path(video_path).open(\"rb\") as video_file:\n task = client.task.create(index_id=self._index_id, file=video_file)\n self._task_id = task.id\n\n # Wait for processing to complete\n task.wait_for_done(sleep_interval=5, callback=self.on_task_update)\n\n if task.status != \"ready\":\n return Message(text=f\"Processing failed with status {task.status}\")\n\n # Store video_id for future use\n self._video_id = task.video_id\n\n # Generate response if message provided\n if self._message:\n self.status = f\"Processing query: {self._message}\"\n self.log(self.status)\n\n response = client.generate.text(\n video_id=self._video_id,\n prompt=self._message,\n temperature=self.temperature,\n )\n return Message(text=response.data)\n\n success_msg = (\n f\"Video processed successfully. You can now ask questions about the video. Video ID: {self._video_id}\"\n )\n return Message(text=success_msg)\n\n except (ValueError, KeyError, IndexCreationError, TaskError, TaskTimeoutError) as e:\n self.log(f\"Error: {e!s}\", \"ERROR\")\n # Clear stored IDs on error\n self._video_id = None\n self._index_id = None\n self._task_id = None\n return Message(text=f\"Error: {e!s}\")\n\n def get_video_id(self) -> Message:\n \"\"\"Return the video ID of the processed video as a Message.\n\n Returns an empty string if no video has been processed.\n \"\"\"\n video_id = self._video_id or \"\"\n return Message(text=video_id)\n" - }, - "index_id": { - "_input_type": "MessageInput", - "advanced": false, - "display_name": "Index ID", - "dynamic": false, - "info": "ID of an existing index to use. If provided, index_name will be ignored.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "index_id", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "index_name": { - "_input_type": "MessageInput", - "advanced": false, - "display_name": "Index Name", - "dynamic": false, - "info": "Name of the index to use. If the index doesn't exist, it will be created.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "index_name", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "message": { - "_input_type": "MultilineInput", - "advanced": false, - "ai_enabled": false, - "copy_field": false, - "display_name": "Prompt", - "dynamic": false, - "info": "Message to chat with the video.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "multiline": true, - "name": "message", - "override_skip": false, - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "model_name": { - "_input_type": "DropdownInput", - "advanced": false, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Model", - "dynamic": false, - "external_options": {}, - "info": "Pegasus model to use for indexing", - "name": "model_name", - "options": [ - "pegasus1.2" - ], - "options_metadata": [], - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "toggle": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "str", - "value": "pegasus1.2" - }, - "temperature": { - "_input_type": "SliderInput", - "advanced": false, - "display_name": "Temperature", - "dynamic": false, - "info": "Controls randomness in responses. Lower values are more deterministic, higher values are more creative.", - "max_label": "", - "max_label_icon": "", - "min_label": "", - "min_label_icon": "", - "name": "temperature", - "override_skip": false, - "placeholder": "", - "range_spec": { - "max": 1.0, - "min": 0.0, - "step": 0.01, - "step_type": "float" - }, - "required": false, - "show": true, - "slider_buttons": false, - "slider_buttons_options": [], - "slider_input": false, - "title_case": false, - "tool_mode": false, - "track_in_telemetry": false, - "type": "slider", - "value": 0.7 - }, - "video_id": { - "_input_type": "MessageInput", - "advanced": false, - "display_name": "Pegasus Video ID", - "dynamic": false, - "info": "Enter a Video ID for a previously indexed video.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "video_id", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "videodata": { - "_input_type": "JSONInput", - "advanced": false, - "display_name": "Video Data", - "dynamic": false, - "info": "Video Data", - "input_types": [ - "Data", - "JSON" - ], - "list": true, - "list_add_label": "Add More", - "name": "videodata", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "other", - "value": "" - } - }, - "tool_mode": false - }, - "TwelveLabsPegasusIndexVideo": { - "base_classes": [ - "JSON" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Index videos using TwelveLabs and add the video_id to metadata.", - "display_name": "TwelveLabs Pegasus Index Video", - "documentation": "https://github.com/twelvelabs-io/twelvelabs-developer-experience/blob/main/integrations/Langflow/TWELVE_LABS_COMPONENTS_README.md", - "edited": false, - "field_order": [ - "videodata", - "api_key", - "model_name", - "index_name", - "index_id" - ], - "frozen": false, - "icon": "TwelveLabs", - "legacy": false, - "metadata": { - "code_hash": "faa14b3d6a18", - "dependencies": { - "dependencies": [ - { - "name": "tenacity", - "version": "8.5.0" - }, - { - "name": "twelvelabs", - "version": "0.4.11" - }, - { - "name": "lfx", - "version": null - } - ], - "total_dependencies": 3 - }, - "module": "lfx.components.twelvelabs.pegasus_index.PegasusIndexVideo" - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Indexed Data", - "group_outputs": false, - "method": "index_videos", - "name": "indexed_data", - "selected": "JSON", - "tool_mode": true, - "types": [ - "JSON" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "api_key": { - "_input_type": "SecretStrInput", - "advanced": false, - "display_name": "TwelveLabs API Key", - "dynamic": false, - "info": "Enter your TwelveLabs API Key.", - "input_types": [], - "load_from_db": true, - "name": "api_key", - "override_skip": false, - "password": true, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "import time\nfrom concurrent.futures import ThreadPoolExecutor\nfrom pathlib import Path\nfrom typing import Any\n\nfrom tenacity import retry, stop_after_attempt, wait_exponential\nfrom twelvelabs import TwelveLabs\n\nfrom lfx.custom import Component\nfrom lfx.inputs import DataInput, DropdownInput, SecretStrInput, StrInput\nfrom lfx.io import Output\nfrom lfx.schema import Data\n\n\nclass TwelveLabsError(Exception):\n \"\"\"Base exception for TwelveLabs errors.\"\"\"\n\n\nclass IndexCreationError(TwelveLabsError):\n \"\"\"Error raised when there's an issue with an index.\"\"\"\n\n\nclass TaskError(TwelveLabsError):\n \"\"\"Error raised when a task fails.\"\"\"\n\n\nclass TaskTimeoutError(TwelveLabsError):\n \"\"\"Error raised when a task times out.\"\"\"\n\n\nclass PegasusIndexVideo(Component):\n \"\"\"Indexes videos using TwelveLabs Pegasus API and adds the video ID to metadata.\"\"\"\n\n display_name = \"TwelveLabs Pegasus Index Video\"\n description = \"Index videos using TwelveLabs and add the video_id to metadata.\"\n icon = \"TwelveLabs\"\n name = \"TwelveLabsPegasusIndexVideo\"\n documentation = \"https://github.com/twelvelabs-io/twelvelabs-developer-experience/blob/main/integrations/Langflow/TWELVE_LABS_COMPONENTS_README.md\"\n\n inputs = [\n DataInput(\n name=\"videodata\",\n display_name=\"Video Data\",\n info=\"Video Data objects (from VideoFile or SplitVideo)\",\n is_list=True,\n required=True,\n ),\n SecretStrInput(\n name=\"api_key\", display_name=\"TwelveLabs API Key\", info=\"Enter your TwelveLabs API Key.\", required=True\n ),\n DropdownInput(\n name=\"model_name\",\n display_name=\"Model\",\n info=\"Pegasus model to use for indexing\",\n options=[\"pegasus1.2\"],\n value=\"pegasus1.2\",\n advanced=False,\n ),\n StrInput(\n name=\"index_name\",\n display_name=\"Index Name\",\n info=\"Name of the index to use. If the index doesn't exist, it will be created.\",\n required=False,\n ),\n StrInput(\n name=\"index_id\",\n display_name=\"Index ID\",\n info=\"ID of an existing index to use. If provided, index_name will be ignored.\",\n required=False,\n ),\n ]\n\n outputs = [\n Output(\n display_name=\"Indexed Data\", name=\"indexed_data\", method=\"index_videos\", output_types=[\"JSON\"], is_list=True\n ),\n ]\n\n def _get_or_create_index(self, client: TwelveLabs) -> tuple[str, str]:\n \"\"\"Get existing index or create new one.\n\n Returns (index_id, index_name).\n \"\"\"\n # First check if index_id is provided and valid\n if hasattr(self, \"index_id\") and self.index_id:\n try:\n index = client.index.retrieve(id=self.index_id)\n except (ValueError, KeyError) as e:\n if not hasattr(self, \"index_name\") or not self.index_name:\n error_msg = \"Invalid index ID provided and no index name specified for fallback\"\n raise IndexCreationError(error_msg) from e\n else:\n return self.index_id, index.name\n\n # If index_name is provided, try to find it\n if hasattr(self, \"index_name\") and self.index_name:\n try:\n # List all indexes and find by name\n indexes = client.index.list()\n for idx in indexes:\n if idx.name == self.index_name:\n return idx.id, idx.name\n\n # If we get here, index wasn't found - create it\n index = client.index.create(\n name=self.index_name,\n models=[\n {\n \"name\": self.model_name if hasattr(self, \"model_name\") else \"pegasus1.2\",\n \"options\": [\"visual\", \"audio\"],\n }\n ],\n )\n except (ValueError, KeyError) as e:\n error_msg = f\"Error with index name {self.index_name}\"\n raise IndexCreationError(error_msg) from e\n else:\n return index.id, index.name\n\n # If we get here, neither index_id nor index_name was provided\n error_msg = \"Either index_name or index_id must be provided\"\n raise IndexCreationError(error_msg)\n\n def on_task_update(self, task: Any, video_path: str) -> None:\n \"\"\"Callback for task status updates.\n\n Updates the component status with the current task status.\n \"\"\"\n video_name = Path(video_path).name\n status_msg = f\"Indexing {video_name}... Status: {task.status}\"\n self.status = status_msg\n\n @retry(stop=stop_after_attempt(5), wait=wait_exponential(multiplier=1, min=5, max=60), reraise=True)\n def _check_task_status(\n self,\n client: TwelveLabs,\n task_id: str,\n video_path: str,\n ) -> Any:\n \"\"\"Check task status once.\n\n Makes a single API call to check the status of a task.\n \"\"\"\n task = client.task.retrieve(id=task_id)\n self.on_task_update(task, video_path)\n return task\n\n def _wait_for_task_completion(\n self, client: TwelveLabs, task_id: str, video_path: str, max_retries: int = 120, sleep_time: int = 10\n ) -> Any:\n \"\"\"Wait for task completion with timeout and improved error handling.\n\n Polls the task status until completion or timeout.\n \"\"\"\n retries = 0\n consecutive_errors = 0\n max_consecutive_errors = 5\n video_name = Path(video_path).name\n\n while retries < max_retries:\n try:\n self.status = f\"Checking task status for {video_name} (attempt {retries + 1})\"\n task = self._check_task_status(client, task_id, video_path)\n\n if task.status == \"ready\":\n self.status = f\"Indexing for {video_name} completed successfully!\"\n return task\n if task.status == \"failed\":\n error_msg = f\"Task failed for {video_name}: {getattr(task, 'error', 'Unknown error')}\"\n self.status = error_msg\n raise TaskError(error_msg)\n if task.status == \"error\":\n error_msg = f\"Task encountered an error for {video_name}: {getattr(task, 'error', 'Unknown error')}\"\n self.status = error_msg\n raise TaskError(error_msg)\n\n time.sleep(sleep_time)\n retries += 1\n elapsed_time = retries * sleep_time\n self.status = f\"Indexing {video_name}... {elapsed_time}s elapsed\"\n\n except (ValueError, KeyError) as e:\n consecutive_errors += 1\n error_msg = f\"Error checking task status for {video_name}: {e!s}\"\n self.status = error_msg\n\n if consecutive_errors >= max_consecutive_errors:\n too_many_errors = f\"Too many consecutive errors checking task status for {video_name}\"\n raise TaskError(too_many_errors) from e\n\n time.sleep(sleep_time * (2**consecutive_errors))\n continue\n\n timeout_msg = f\"Timeout waiting for indexing of {video_name} after {max_retries * sleep_time} seconds\"\n self.status = timeout_msg\n raise TaskTimeoutError(timeout_msg)\n\n def _upload_video(self, client: TwelveLabs, video_path: str, index_id: str) -> str:\n \"\"\"Upload a single video and return its task ID.\n\n Uploads a video file to the specified index and returns the task ID.\n \"\"\"\n video_name = Path(video_path).name\n with Path(video_path).open(\"rb\") as video_file:\n self.status = f\"Uploading {video_name} to index {index_id}...\"\n task = client.task.create(index_id=index_id, file=video_file)\n task_id = task.id\n self.status = f\"Upload complete for {video_name}. Task ID: {task_id}\"\n return task_id\n\n def index_videos(self) -> list[Data]:\n \"\"\"Indexes each video and adds the video_id to its metadata.\"\"\"\n if not self.videodata:\n self.status = \"No video data provided.\"\n return []\n\n if not self.api_key:\n error_msg = \"TwelveLabs API Key is required\"\n raise IndexCreationError(error_msg)\n\n if not (hasattr(self, \"index_name\") and self.index_name) and not (hasattr(self, \"index_id\") and self.index_id):\n error_msg = \"Either index_name or index_id must be provided\"\n raise IndexCreationError(error_msg)\n\n client = TwelveLabs(api_key=self.api_key)\n indexed_data_list: list[Data] = []\n\n # Get or create the index\n try:\n index_id, index_name = self._get_or_create_index(client)\n self.status = f\"Using index: {index_name} (ID: {index_id})\"\n except IndexCreationError as e:\n self.status = f\"Failed to get/create TwelveLabs index: {e!s}\"\n raise\n\n # First, validate all videos and create a list of valid ones\n valid_videos: list[tuple[Data, str]] = []\n for video_data_item in self.videodata:\n if not isinstance(video_data_item, Data):\n self.status = f\"Skipping invalid data item: {video_data_item}\"\n continue\n\n video_info = video_data_item.data\n if not isinstance(video_info, dict):\n self.status = f\"Skipping item with invalid data structure: {video_info}\"\n continue\n\n video_path = video_info.get(\"text\")\n if not video_path or not isinstance(video_path, str):\n self.status = f\"Skipping item with missing or invalid video path: {video_info}\"\n continue\n\n if not Path(video_path).exists():\n self.status = f\"Video file not found, skipping: {video_path}\"\n continue\n\n valid_videos.append((video_data_item, video_path))\n\n if not valid_videos:\n self.status = \"No valid videos to process.\"\n return []\n\n # Upload all videos first and collect their task IDs\n upload_tasks: list[tuple[Data, str, str]] = [] # (data_item, video_path, task_id)\n for data_item, video_path in valid_videos:\n try:\n task_id = self._upload_video(client, video_path, index_id)\n upload_tasks.append((data_item, video_path, task_id))\n except (ValueError, KeyError) as e:\n self.status = f\"Failed to upload {video_path}: {e!s}\"\n continue\n\n # Now check all tasks in parallel using a thread pool\n with ThreadPoolExecutor(max_workers=min(10, len(upload_tasks))) as executor:\n futures = []\n for data_item, video_path, task_id in upload_tasks:\n future = executor.submit(self._wait_for_task_completion, client, task_id, video_path)\n futures.append((data_item, video_path, future))\n\n # Process results as they complete\n for data_item, video_path, future in futures:\n try:\n completed_task = future.result()\n if completed_task.status == \"ready\":\n video_id = completed_task.video_id\n video_name = Path(video_path).name\n self.status = f\"Video {video_name} indexed successfully. Video ID: {video_id}\"\n\n # Add video_id to the metadata\n video_info = data_item.data\n if \"metadata\" not in video_info:\n video_info[\"metadata\"] = {}\n elif not isinstance(video_info[\"metadata\"], dict):\n self.status = f\"Warning: Overwriting non-dict metadata for {video_path}\"\n video_info[\"metadata\"] = {}\n\n video_info[\"metadata\"].update(\n {\"video_id\": video_id, \"index_id\": index_id, \"index_name\": index_name}\n )\n\n updated_data_item = Data(data=video_info)\n indexed_data_list.append(updated_data_item)\n except (TaskError, TaskTimeoutError) as e:\n self.status = f\"Failed to process {video_path}: {e!s}\"\n\n if not indexed_data_list:\n self.status = \"No videos were successfully indexed.\"\n else:\n self.status = f\"Finished indexing {len(indexed_data_list)}/{len(self.videodata)} videos.\"\n\n return indexed_data_list\n" - }, - "index_id": { - "_input_type": "StrInput", - "advanced": false, - "display_name": "Index ID", - "dynamic": false, - "info": "ID of an existing index to use. If provided, index_name will be ignored.", - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "index_id", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "index_name": { - "_input_type": "StrInput", - "advanced": false, - "display_name": "Index Name", - "dynamic": false, - "info": "Name of the index to use. If the index doesn't exist, it will be created.", - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "index_name", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "model_name": { - "_input_type": "DropdownInput", - "advanced": false, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Model", - "dynamic": false, - "external_options": {}, - "info": "Pegasus model to use for indexing", - "name": "model_name", - "options": [ - "pegasus1.2" - ], - "options_metadata": [], - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "toggle": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "str", - "value": "pegasus1.2" - }, - "videodata": { - "_input_type": "JSONInput", - "advanced": false, - "display_name": "Video Data", - "dynamic": false, - "info": "Video Data objects (from VideoFile or SplitVideo)", - "input_types": [ - "Data", - "JSON" - ], - "list": true, - "list_add_label": "Add More", - "name": "videodata", - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "other", - "value": "" - } - }, - "tool_mode": false - }, - "TwelveLabsTextEmbeddings": { - "base_classes": [ - "Embeddings" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Generate embeddings using TwelveLabs text embedding models.", - "display_name": "TwelveLabs Text Embeddings", - "documentation": "https://github.com/twelvelabs-io/twelvelabs-developer-experience/blob/main/integrations/Langflow/TWELVE_LABS_COMPONENTS_README.md", - "edited": false, - "field_order": [ - "api_key", - "model", - "max_retries", - "request_timeout" - ], - "frozen": false, - "icon": "TwelveLabs", - "legacy": false, - "metadata": { - "code_hash": "5c7501f5088c", - "dependencies": { - "dependencies": [ - { - "name": "twelvelabs", - "version": "0.4.11" - }, - { - "name": "lfx", - "version": null - } - ], - "total_dependencies": 2 - }, - "module": "lfx.components.twelvelabs.text_embeddings.TwelveLabsTextEmbeddingsComponent" - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Embedding Model", - "group_outputs": false, - "method": "build_embeddings", - "name": "embeddings", - "selected": "Embeddings", - "tool_mode": true, - "types": [ - "Embeddings" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "api_key": { - "_input_type": "SecretStrInput", - "advanced": false, - "display_name": "TwelveLabs API Key", - "dynamic": false, - "info": "", - "input_types": [], - "load_from_db": true, - "name": "api_key", - "override_skip": false, - "password": true, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "track_in_telemetry": false, - "type": "str", - "value": "TWELVELABS_API_KEY" - }, - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from twelvelabs import TwelveLabs\n\nfrom lfx.base.embeddings.model import LCEmbeddingsModel\nfrom lfx.field_typing import Embeddings\nfrom lfx.io import DropdownInput, FloatInput, IntInput, SecretStrInput\n\n\nclass TwelveLabsTextEmbeddings(Embeddings):\n def __init__(self, api_key: str, model: str) -> None:\n self.client = TwelveLabs(api_key=api_key)\n self.model = model\n\n def embed_documents(self, texts: list[str]) -> list[list[float]]:\n all_embeddings: list[list[float]] = []\n for text in texts:\n if not text:\n continue\n\n result = self.client.embed.create(model_name=self.model, text=text)\n\n if result.text_embedding and result.text_embedding.segments:\n for segment in result.text_embedding.segments:\n all_embeddings.append([float(x) for x in segment.embeddings_float])\n break # Only take first segment for now\n\n return all_embeddings\n\n def embed_query(self, text: str) -> list[float]:\n result = self.client.embed.create(model_name=self.model, text=text)\n\n if result.text_embedding and result.text_embedding.segments:\n return [float(x) for x in result.text_embedding.segments[0].embeddings_float]\n return []\n\n\nclass TwelveLabsTextEmbeddingsComponent(LCEmbeddingsModel):\n display_name = \"TwelveLabs Text Embeddings\"\n description = \"Generate embeddings using TwelveLabs text embedding models.\"\n icon = \"TwelveLabs\"\n name = \"TwelveLabsTextEmbeddings\"\n documentation = \"https://github.com/twelvelabs-io/twelvelabs-developer-experience/blob/main/integrations/Langflow/TWELVE_LABS_COMPONENTS_README.md\"\n\n inputs = [\n SecretStrInput(name=\"api_key\", display_name=\"TwelveLabs API Key\", value=\"TWELVELABS_API_KEY\", required=True),\n DropdownInput(\n name=\"model\",\n display_name=\"Model\",\n advanced=False,\n options=[\"Marengo-retrieval-2.7\"],\n value=\"Marengo-retrieval-2.7\",\n ),\n IntInput(name=\"max_retries\", display_name=\"Max Retries\", value=3, advanced=True),\n FloatInput(name=\"request_timeout\", display_name=\"Request Timeout\", advanced=True),\n ]\n\n def build_embeddings(self) -> Embeddings:\n return TwelveLabsTextEmbeddings(api_key=self.api_key, model=self.model)\n" - }, - "max_retries": { - "_input_type": "IntInput", - "advanced": true, - "display_name": "Max Retries", - "dynamic": false, - "info": "", - "list": false, - "list_add_label": "Add More", - "name": "max_retries", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "int", - "value": 3 - }, - "model": { - "_input_type": "DropdownInput", - "advanced": false, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Model", - "dynamic": false, - "external_options": {}, - "info": "", - "name": "model", - "options": [ - "Marengo-retrieval-2.7" - ], - "options_metadata": [], - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "toggle": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "str", - "value": "Marengo-retrieval-2.7" - }, - "request_timeout": { - "_input_type": "FloatInput", - "advanced": true, - "display_name": "Request Timeout", - "dynamic": false, - "info": "", - "list": false, - "list_add_label": "Add More", - "name": "request_timeout", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "float", - "value": "" - } - }, - "tool_mode": false - }, - "TwelveLabsVideoEmbeddings": { - "base_classes": [ - "Embeddings" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Generate embeddings from videos using TwelveLabs video embedding models.", - "display_name": "TwelveLabs Video Embeddings", - "documentation": "https://github.com/twelvelabs-io/twelvelabs-developer-experience/blob/main/integrations/Langflow/TWELVE_LABS_COMPONENTS_README.md", - "edited": false, - "field_order": [ - "api_key", - "model_name", - "request_timeout" - ], - "frozen": false, - "icon": "TwelveLabs", - "legacy": false, - "metadata": { - "code_hash": "294e3539629c", - "dependencies": { - "dependencies": [ - { - "name": "twelvelabs", - "version": "0.4.11" - }, - { - "name": "lfx", - "version": null - } - ], - "total_dependencies": 2 - }, - "module": "lfx.components.twelvelabs.video_embeddings.TwelveLabsVideoEmbeddingsComponent" - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Embedding Model", - "group_outputs": false, - "method": "build_embeddings", - "name": "embeddings", - "selected": "Embeddings", - "tool_mode": true, - "types": [ - "Embeddings" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "api_key": { - "_input_type": "SecretStrInput", - "advanced": false, - "display_name": "TwelveLabs API Key", - "dynamic": false, - "info": "", - "input_types": [], - "load_from_db": true, - "name": "api_key", - "override_skip": false, - "password": true, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "track_in_telemetry": false, - "type": "str", - "value": "" - }, - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "import time\nfrom pathlib import Path\nfrom typing import Any, cast\n\nfrom twelvelabs import TwelveLabs\n\nfrom lfx.base.embeddings.model import LCEmbeddingsModel\nfrom lfx.field_typing import Embeddings\nfrom lfx.io import DropdownInput, IntInput, SecretStrInput\n\n\nclass TwelveLabsVideoEmbeddings(Embeddings):\n def __init__(self, api_key: str, model_name: str = \"Marengo-retrieval-2.7\") -> None:\n self.client = TwelveLabs(api_key=api_key)\n self.model_name = model_name\n\n def _wait_for_task_completion(self, task_id: str) -> Any:\n while True:\n result = self.client.embed.task.retrieve(id=task_id)\n if result.status == \"ready\":\n return result\n time.sleep(5)\n\n def embed_documents(self, texts: list[str]) -> list[list[float]]:\n embeddings: list[list[float]] = []\n for text in texts:\n video_path = text.page_content if hasattr(text, \"page_content\") else str(text)\n result = self.embed_video(video_path)\n\n # First try to use video embedding, then fall back to clip embedding if available\n if result[\"video_embedding\"] is not None:\n embeddings.append(cast(\"list[float]\", result[\"video_embedding\"]))\n elif result[\"clip_embeddings\"] and len(result[\"clip_embeddings\"]) > 0:\n embeddings.append(cast(\"list[float]\", result[\"clip_embeddings\"][0]))\n else:\n # If neither is available, raise an error\n error_msg = \"No embeddings were generated for the video\"\n raise ValueError(error_msg)\n\n return embeddings\n\n def embed_query(self, text: str) -> list[float]:\n video_path = text.page_content if hasattr(text, \"page_content\") else str(text)\n result = self.embed_video(video_path)\n\n # First try to use video embedding, then fall back to clip embedding if available\n if result[\"video_embedding\"] is not None:\n return cast(\"list[float]\", result[\"video_embedding\"])\n if result[\"clip_embeddings\"] and len(result[\"clip_embeddings\"]) > 0:\n return cast(\"list[float]\", result[\"clip_embeddings\"][0])\n # If neither is available, raise an error\n error_msg = \"No embeddings were generated for the video\"\n raise ValueError(error_msg)\n\n def embed_video(self, video_path: str) -> dict[str, list[float] | list[list[float]]]:\n file_path = Path(video_path)\n with file_path.open(\"rb\") as video_file:\n task = self.client.embed.task.create(\n model_name=self.model_name,\n video_file=video_file,\n video_embedding_scopes=[\"video\", \"clip\"],\n )\n\n result = self._wait_for_task_completion(task.id)\n\n video_embedding: dict[str, list[float] | list[list[float]]] = {\n \"video_embedding\": [], # Initialize as empty list instead of None\n \"clip_embeddings\": [],\n }\n\n if hasattr(result.video_embedding, \"segments\") and result.video_embedding.segments:\n for seg in result.video_embedding.segments:\n # Check for embeddings_float attribute (this is the correct attribute name)\n if hasattr(seg, \"embeddings_float\") and seg.embedding_scope == \"video\":\n # Convert to list of floats\n video_embedding[\"video_embedding\"] = [float(x) for x in seg.embeddings_float]\n\n return video_embedding\n\n\nclass TwelveLabsVideoEmbeddingsComponent(LCEmbeddingsModel):\n display_name = \"TwelveLabs Video Embeddings\"\n description = \"Generate embeddings from videos using TwelveLabs video embedding models.\"\n name = \"TwelveLabsVideoEmbeddings\"\n icon = \"TwelveLabs\"\n documentation = \"https://github.com/twelvelabs-io/twelvelabs-developer-experience/blob/main/integrations/Langflow/TWELVE_LABS_COMPONENTS_README.md\"\n inputs = [\n SecretStrInput(name=\"api_key\", display_name=\"TwelveLabs API Key\", required=True),\n DropdownInput(\n name=\"model_name\",\n display_name=\"Model\",\n advanced=False,\n options=[\"Marengo-retrieval-2.7\"],\n value=\"Marengo-retrieval-2.7\",\n ),\n IntInput(name=\"request_timeout\", display_name=\"Request Timeout\", advanced=True),\n ]\n\n def build_embeddings(self) -> Embeddings:\n return TwelveLabsVideoEmbeddings(api_key=self.api_key, model_name=self.model_name)\n" - }, - "model_name": { - "_input_type": "DropdownInput", - "advanced": false, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Model", - "dynamic": false, - "external_options": {}, - "info": "", - "name": "model_name", - "options": [ - "Marengo-retrieval-2.7" - ], - "options_metadata": [], - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "toggle": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "str", - "value": "Marengo-retrieval-2.7" - }, - "request_timeout": { - "_input_type": "IntInput", - "advanced": true, - "display_name": "Request Timeout", - "dynamic": false, - "info": "", - "list": false, - "list_add_label": "Add More", - "name": "request_timeout", - "override_skip": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": true, - "type": "int", - "value": 0 - } - }, - "tool_mode": false - }, - "VideoFile": { - "base_classes": [ - "Table" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Load a video file in common video formats.", - "display_name": "Video File", - "documentation": "https://github.com/twelvelabs-io/twelvelabs-developer-experience/blob/main/integrations/Langflow/TWELVE_LABS_COMPONENTS_README.md", - "edited": false, - "field_order": [ - "file_path" - ], - "frozen": false, - "icon": "TwelveLabs", - "legacy": false, - "metadata": { - "code_hash": "8dc827e42377", - "dependencies": { - "dependencies": [ - { - "name": "lfx", - "version": null - } - ], - "total_dependencies": 1 - }, - "module": "lfx.components.twelvelabs.video_file.VideoFileComponent" - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Files", - "group_outputs": false, - "method": "load_files", - "name": "dataframe", - "selected": "Table", - "tool_mode": true, - "types": [ - "Table" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from pathlib import Path\n\nfrom lfx.base.data import BaseFileComponent\nfrom lfx.io import FileInput\nfrom lfx.schema import Data, DataFrame\nfrom lfx.utils.validate_cloud import raise_error_if_astra_cloud_disable_component\n\ndisable_component_in_astra_cloud_msg = (\n \"Video processing is not supported in Astra cloud environment. \"\n \"Video components require local file system access for processing. \"\n \"Please use local storage mode or process videos locally before uploading.\"\n)\n\n\nclass VideoFileComponent(BaseFileComponent):\n \"\"\"Handles loading and processing of video files.\n\n This component supports processing video files in common video formats.\n \"\"\"\n\n display_name = \"Video File\"\n description = \"Load a video file in common video formats.\"\n icon = \"TwelveLabs\"\n name = \"VideoFile\"\n documentation = \"https://github.com/twelvelabs-io/twelvelabs-developer-experience/blob/main/integrations/Langflow/TWELVE_LABS_COMPONENTS_README.md\"\n\n VALID_EXTENSIONS = [\n # Common video formats\n \"mp4\",\n \"avi\",\n \"mov\",\n \"mkv\",\n \"webm\",\n \"flv\",\n \"wmv\",\n \"mpg\",\n \"mpeg\",\n \"m4v\",\n \"3gp\",\n \"3g2\",\n \"m2v\",\n # Professional video formats\n \"mxf\",\n \"dv\",\n \"vob\",\n # Additional video formats\n \"ogv\",\n \"rm\",\n \"rmvb\",\n \"amv\",\n \"divx\",\n \"m2ts\",\n \"mts\",\n \"ts\",\n \"qt\",\n \"yuv\",\n \"y4m\",\n ]\n\n inputs = [\n FileInput(\n display_name=\"Video File\",\n name=\"file_path\",\n file_types=[\n # Common video formats\n \"mp4\",\n \"avi\",\n \"mov\",\n \"mkv\",\n \"webm\",\n \"flv\",\n \"wmv\",\n \"mpg\",\n \"mpeg\",\n \"m4v\",\n \"3gp\",\n \"3g2\",\n \"m2v\",\n # Professional video formats\n \"mxf\",\n \"dv\",\n \"vob\",\n # Additional video formats\n \"ogv\",\n \"rm\",\n \"rmvb\",\n \"amv\",\n \"divx\",\n \"m2ts\",\n \"mts\",\n \"ts\",\n \"qt\",\n \"yuv\",\n \"y4m\",\n ],\n required=True,\n info=\"Upload a video file in any common video format supported by ffmpeg\",\n ),\n ]\n\n outputs = [\n *BaseFileComponent.get_base_outputs(),\n ]\n\n def process_files(self, file_list: list[BaseFileComponent.BaseFile]) -> list[BaseFileComponent.BaseFile]:\n \"\"\"Process video files.\"\"\"\n # Check if we're in Astra cloud environment and raise an error if we are.\n raise_error_if_astra_cloud_disable_component(disable_component_in_astra_cloud_msg)\n self.log(f\"DEBUG: Processing video files: {len(file_list)}\")\n\n if not file_list:\n msg = \"No files to process.\"\n raise ValueError(msg)\n\n processed_files = []\n for file in file_list:\n try:\n file_path = str(file.path)\n self.log(f\"DEBUG: Processing video file: {file_path}\")\n\n # Verify file exists\n file_path_obj = Path(file_path)\n if not file_path_obj.exists():\n error_msg = f\"Video file not found: {file_path}\"\n raise FileNotFoundError(error_msg)\n\n # Verify extension\n if not file_path.lower().endswith(tuple(self.VALID_EXTENSIONS)):\n error_msg = f\"Invalid file type. Expected: {', '.join(self.VALID_EXTENSIONS)}\"\n raise ValueError(error_msg)\n\n # Create a dictionary instead of a Document\n doc_data = {\"text\": file_path, \"metadata\": {\"source\": file_path, \"type\": \"video\"}}\n\n # Pass the dictionary to Data\n file.data = Data(data=doc_data)\n\n self.log(f\"DEBUG: Created data: {doc_data}\")\n processed_files.append(file)\n\n except Exception as e:\n self.log(f\"Error processing video file: {e!s}\", \"ERROR\")\n raise\n\n return processed_files\n\n def load_files(self) -> DataFrame:\n \"\"\"Load video files and return a list of Data objects.\"\"\"\n # Check if we're in Astra cloud environment and raise an error if we are.\n raise_error_if_astra_cloud_disable_component(disable_component_in_astra_cloud_msg)\n\n try:\n self.log(\"DEBUG: Starting video file load\")\n if not hasattr(self, \"file_path\") or not self.file_path:\n self.log(\"DEBUG: No video file path provided\")\n return DataFrame()\n\n self.log(f\"DEBUG: Loading video from path: {self.file_path}\")\n\n # Verify file exists\n file_path_obj = Path(self.file_path)\n if not file_path_obj.exists():\n self.log(f\"DEBUG: Video file not found at path: {self.file_path}\")\n return DataFrame()\n\n # Verify file size\n file_size = file_path_obj.stat().st_size\n self.log(f\"DEBUG: Video file size: {file_size} bytes\")\n\n # Create a proper Data object with the video path\n video_data = {\n \"text\": self.file_path,\n \"metadata\": {\"source\": self.file_path, \"type\": \"video\", \"size\": file_size},\n }\n\n self.log(f\"DEBUG: Created video data: {video_data}\")\n result = DataFrame(data=[video_data])\n\n # Log the result to verify it's a proper Data object\n self.log(\"DEBUG: Returning list with Data objects\")\n except (FileNotFoundError, PermissionError, OSError) as e:\n self.log(f\"DEBUG: File error in video load_files: {e!s}\", \"ERROR\")\n return DataFrame()\n except ImportError as e:\n self.log(f\"DEBUG: Import error in video load_files: {e!s}\", \"ERROR\")\n return DataFrame()\n except (ValueError, TypeError) as e:\n self.log(f\"DEBUG: Value or type error in video load_files: {e!s}\", \"ERROR\")\n return DataFrame()\n else:\n return result\n" - }, - "file_path": { - "_input_type": "FileInput", - "advanced": false, - "display_name": "Video File", - "dynamic": false, - "fileTypes": [ - "mp4", - "avi", - "mov", - "mkv", - "webm", - "flv", - "wmv", - "mpg", - "mpeg", - "m4v", - "3gp", - "3g2", - "m2v", - "mxf", - "dv", - "vob", - "ogv", - "rm", - "rmvb", - "amv", - "divx", - "m2ts", - "mts", - "ts", - "qt", - "yuv", - "y4m" - ], - "file_path": "", - "info": "Upload a video file in any common video format supported by ffmpeg", - "list": false, - "list_add_label": "Add More", - "name": "file_path", - "override_skip": false, - "placeholder": "", - "required": true, - "show": true, - "temp_file": false, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "track_in_telemetry": false, - "type": "file", - "value": "" - } - }, - "tool_mode": false - } - } - ], [ "utilities", { @@ -89632,9 +85399,9 @@ ] ], "metadata": { - "num_components": 263, - "num_modules": 45 + "num_components": 242, + "num_modules": 42 }, - "sha256": "ec9dd582be9de938a0d202d311300e0c387262a56b57acc61f85abcb5c6340ab", + "sha256": "da72774e89b94a022f945d4a404c8c939d4054dd7db559282daa5dc41c2912c7", "version": "1.11.0" } diff --git a/src/lfx/src/lfx/components/cleanlab/__init__.py b/src/lfx/src/lfx/components/cleanlab/__init__.py index 4473e5d2f7..c7b370a290 100644 --- a/src/lfx/src/lfx/components/cleanlab/__init__.py +++ b/src/lfx/src/lfx/components/cleanlab/__init__.py @@ -1,40 +1,22 @@ -from __future__ import annotations +# lfx-bundles-shim +"""Compatibility shim: lfx.components.cleanlab moved to lfx-bundles. -from typing import TYPE_CHECKING, Any +This module re-points to the installed bundle distribution. It contains +no component implementations and no third-party dependencies, and is +removed once the deprecation window closes (M4). +""" -from lfx.components._importing import import_mod +import importlib +import sys -if TYPE_CHECKING: - from .cleanlab_evaluator import CleanlabEvaluator - from .cleanlab_rag_evaluator import CleanlabRAGEvaluator - from .cleanlab_remediator import CleanlabRemediator - -_dynamic_imports = { - "CleanlabEvaluator": "cleanlab_evaluator", - "CleanlabRAGEvaluator": "cleanlab_rag_evaluator", - "CleanlabRemediator": "cleanlab_remediator", -} - -__all__ = [ - "CleanlabEvaluator", - "CleanlabRAGEvaluator", - "CleanlabRemediator", -] - - -def __getattr__(attr_name: str) -> Any: - """Lazily import cleanlab components on attribute access.""" - if attr_name not in _dynamic_imports: - msg = f"module '{__name__}' has no attribute '{attr_name}'" - raise AttributeError(msg) - try: - result = import_mod(attr_name, _dynamic_imports[attr_name], __spec__.parent) - except (ModuleNotFoundError, ImportError, AttributeError) as e: - msg = f"Could not import '{attr_name}' from '{__name__}': {e}" - raise AttributeError(msg) from e - globals()[attr_name] = result - return result - - -def __dir__() -> list[str]: - return list(__all__) +try: + sys.modules[__name__] = importlib.import_module("lfx_bundles.cleanlab") +except ModuleNotFoundError as exc: + if exc.name is not None and (exc.name == "lfx_bundles" or exc.name.startswith("lfx_bundles.")): + msg = ( + "The 'cleanlab' components moved to the 'lfx-bundles' distribution. " + "Install it with: pip install lfx-bundles " + "(or 'pip install langflow', which bundles it)." + ) + raise ModuleNotFoundError(msg, name="lfx_bundles") from exc + raise diff --git a/src/lfx/src/lfx/components/jigsawstack/__init__.py b/src/lfx/src/lfx/components/jigsawstack/__init__.py index c32d56ee71..6a0d41192a 100644 --- a/src/lfx/src/lfx/components/jigsawstack/__init__.py +++ b/src/lfx/src/lfx/components/jigsawstack/__init__.py @@ -1,23 +1,22 @@ -from .ai_scrape import JigsawStackAIScraperComponent -from .ai_web_search import JigsawStackAIWebSearchComponent -from .file_read import JigsawStackFileReadComponent -from .file_upload import JigsawStackFileUploadComponent -from .image_generation import JigsawStackImageGenerationComponent -from .nsfw import JigsawStackNSFWComponent -from .object_detection import JigsawStackObjectDetectionComponent -from .sentiment import JigsawStackSentimentComponent -from .text_to_sql import JigsawStackTextToSQLComponent -from .vocr import JigsawStackVOCRComponent +# lfx-bundles-shim +"""Compatibility shim: lfx.components.jigsawstack moved to lfx-bundles. -__all__ = [ - "JigsawStackAIScraperComponent", - "JigsawStackAIWebSearchComponent", - "JigsawStackFileReadComponent", - "JigsawStackFileUploadComponent", - "JigsawStackImageGenerationComponent", - "JigsawStackNSFWComponent", - "JigsawStackObjectDetectionComponent", - "JigsawStackSentimentComponent", - "JigsawStackTextToSQLComponent", - "JigsawStackVOCRComponent", -] +This module re-points to the installed bundle distribution. It contains +no component implementations and no third-party dependencies, and is +removed once the deprecation window closes (M4). +""" + +import importlib +import sys + +try: + sys.modules[__name__] = importlib.import_module("lfx_bundles.jigsawstack") +except ModuleNotFoundError as exc: + if exc.name is not None and (exc.name == "lfx_bundles" or exc.name.startswith("lfx_bundles.")): + msg = ( + "The 'jigsawstack' components moved to the 'lfx-bundles' distribution. " + "Install it with: pip install lfx-bundles " + "(or 'pip install langflow', which bundles it)." + ) + raise ModuleNotFoundError(msg, name="lfx_bundles") from exc + raise diff --git a/src/lfx/src/lfx/components/twelvelabs/__init__.py b/src/lfx/src/lfx/components/twelvelabs/__init__.py index 526274a6b4..fcb2f636c0 100644 --- a/src/lfx/src/lfx/components/twelvelabs/__init__.py +++ b/src/lfx/src/lfx/components/twelvelabs/__init__.py @@ -1,52 +1,22 @@ -from __future__ import annotations +# lfx-bundles-shim +"""Compatibility shim: lfx.components.twelvelabs moved to lfx-bundles. -from typing import TYPE_CHECKING, Any +This module re-points to the installed bundle distribution. It contains +no component implementations and no third-party dependencies, and is +removed once the deprecation window closes (M4). +""" -from lfx.components._importing import import_mod +import importlib +import sys -if TYPE_CHECKING: - from .convert_astra_results import ConvertAstraToTwelveLabs - from .pegasus_index import PegasusIndexVideo - from .split_video import SplitVideoComponent - from .text_embeddings import TwelveLabsTextEmbeddingsComponent - from .twelvelabs_pegasus import TwelveLabsPegasus - from .video_embeddings import TwelveLabsVideoEmbeddingsComponent - from .video_file import VideoFileComponent - -_dynamic_imports = { - "ConvertAstraToTwelveLabs": "convert_astra_results", - "PegasusIndexVideo": "pegasus_index", - "SplitVideoComponent": "split_video", - "TwelveLabsPegasus": "twelvelabs_pegasus", - "TwelveLabsTextEmbeddingsComponent": "text_embeddings", - "TwelveLabsVideoEmbeddingsComponent": "video_embeddings", - "VideoFileComponent": "video_file", -} - -__all__ = [ - "ConvertAstraToTwelveLabs", - "PegasusIndexVideo", - "SplitVideoComponent", - "TwelveLabsPegasus", - "TwelveLabsTextEmbeddingsComponent", - "TwelveLabsVideoEmbeddingsComponent", - "VideoFileComponent", -] - - -def __getattr__(attr_name: str) -> Any: - """Lazily import twelvelabs components on attribute access.""" - if attr_name not in _dynamic_imports: - msg = f"module '{__name__}' has no attribute '{attr_name}'" - raise AttributeError(msg) - try: - result = import_mod(attr_name, _dynamic_imports[attr_name], __spec__.parent) - except (ModuleNotFoundError, ImportError, AttributeError) as e: - msg = f"Could not import '{attr_name}' from '{__name__}': {e}" - raise AttributeError(msg) from e - globals()[attr_name] = result - return result - - -def __dir__() -> list[str]: - return list(__all__) +try: + sys.modules[__name__] = importlib.import_module("lfx_bundles.twelvelabs") +except ModuleNotFoundError as exc: + if exc.name is not None and (exc.name == "lfx_bundles" or exc.name.startswith("lfx_bundles.")): + msg = ( + "The 'twelvelabs' components moved to the 'lfx-bundles' distribution. " + "Install it with: pip install lfx-bundles " + "(or 'pip install langflow', which bundles it)." + ) + raise ModuleNotFoundError(msg, name="lfx_bundles") from exc + raise diff --git a/src/lfx/src/lfx/extension/migration/migration_table.json b/src/lfx/src/lfx/extension/migration/migration_table.json index d10a7824a6..6ee4ffe3b7 100644 --- a/src/lfx/src/lfx/extension/migration/migration_table.json +++ b/src/lfx/src/lfx/extension/migration/migration_table.json @@ -2120,6 +2120,426 @@ "legacy_slot": "ext:toolguard:PoliciesComponent@official-pre-a", "target": "ext:toolguard:PoliciesComponent@official", "added_in": "1.11.0" + }, + { + "bare_class_name": "CleanlabEvaluator", + "target": "ext:cleanlab:CleanlabEvaluator@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.cleanlab.cleanlab_evaluator.CleanlabEvaluator", + "target": "ext:cleanlab:CleanlabEvaluator@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.cleanlab.CleanlabEvaluator", + "target": "ext:cleanlab:CleanlabEvaluator@official", + "added_in": "1.11.0" + }, + { + "legacy_slot": "ext:cleanlab:CleanlabEvaluator@official-pre-a", + "target": "ext:cleanlab:CleanlabEvaluator@official", + "added_in": "1.11.0" + }, + { + "bare_class_name": "CleanlabRAGEvaluator", + "target": "ext:cleanlab:CleanlabRAGEvaluator@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.cleanlab.cleanlab_rag_evaluator.CleanlabRAGEvaluator", + "target": "ext:cleanlab:CleanlabRAGEvaluator@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.cleanlab.CleanlabRAGEvaluator", + "target": "ext:cleanlab:CleanlabRAGEvaluator@official", + "added_in": "1.11.0" + }, + { + "legacy_slot": "ext:cleanlab:CleanlabRAGEvaluator@official-pre-a", + "target": "ext:cleanlab:CleanlabRAGEvaluator@official", + "added_in": "1.11.0" + }, + { + "bare_class_name": "CleanlabRemediator", + "target": "ext:cleanlab:CleanlabRemediator@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.cleanlab.cleanlab_remediator.CleanlabRemediator", + "target": "ext:cleanlab:CleanlabRemediator@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.cleanlab.CleanlabRemediator", + "target": "ext:cleanlab:CleanlabRemediator@official", + "added_in": "1.11.0" + }, + { + "legacy_slot": "ext:cleanlab:CleanlabRemediator@official-pre-a", + "target": "ext:cleanlab:CleanlabRemediator@official", + "added_in": "1.11.0" + }, + { + "bare_class_name": "ConvertAstraToTwelveLabs", + "target": "ext:twelvelabs:ConvertAstraToTwelveLabs@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.twelvelabs.convert_astra_results.ConvertAstraToTwelveLabs", + "target": "ext:twelvelabs:ConvertAstraToTwelveLabs@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.twelvelabs.ConvertAstraToTwelveLabs", + "target": "ext:twelvelabs:ConvertAstraToTwelveLabs@official", + "added_in": "1.11.0" + }, + { + "legacy_slot": "ext:twelvelabs:ConvertAstraToTwelveLabs@official-pre-a", + "target": "ext:twelvelabs:ConvertAstraToTwelveLabs@official", + "added_in": "1.11.0" + }, + { + "bare_class_name": "PegasusIndexVideo", + "target": "ext:twelvelabs:PegasusIndexVideo@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.twelvelabs.pegasus_index.PegasusIndexVideo", + "target": "ext:twelvelabs:PegasusIndexVideo@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.twelvelabs.PegasusIndexVideo", + "target": "ext:twelvelabs:PegasusIndexVideo@official", + "added_in": "1.11.0" + }, + { + "legacy_slot": "ext:twelvelabs:PegasusIndexVideo@official-pre-a", + "target": "ext:twelvelabs:PegasusIndexVideo@official", + "added_in": "1.11.0" + }, + { + "bare_class_name": "SplitVideoComponent", + "target": "ext:twelvelabs:SplitVideoComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.twelvelabs.split_video.SplitVideoComponent", + "target": "ext:twelvelabs:SplitVideoComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.twelvelabs.SplitVideoComponent", + "target": "ext:twelvelabs:SplitVideoComponent@official", + "added_in": "1.11.0" + }, + { + "legacy_slot": "ext:twelvelabs:SplitVideoComponent@official-pre-a", + "target": "ext:twelvelabs:SplitVideoComponent@official", + "added_in": "1.11.0" + }, + { + "bare_class_name": "TwelveLabsTextEmbeddingsComponent", + "target": "ext:twelvelabs:TwelveLabsTextEmbeddingsComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.twelvelabs.text_embeddings.TwelveLabsTextEmbeddingsComponent", + "target": "ext:twelvelabs:TwelveLabsTextEmbeddingsComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.twelvelabs.TwelveLabsTextEmbeddingsComponent", + "target": "ext:twelvelabs:TwelveLabsTextEmbeddingsComponent@official", + "added_in": "1.11.0" + }, + { + "legacy_slot": "ext:twelvelabs:TwelveLabsTextEmbeddingsComponent@official-pre-a", + "target": "ext:twelvelabs:TwelveLabsTextEmbeddingsComponent@official", + "added_in": "1.11.0" + }, + { + "bare_class_name": "TwelveLabsPegasus", + "target": "ext:twelvelabs:TwelveLabsPegasus@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.twelvelabs.twelvelabs_pegasus.TwelveLabsPegasus", + "target": "ext:twelvelabs:TwelveLabsPegasus@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.twelvelabs.TwelveLabsPegasus", + "target": "ext:twelvelabs:TwelveLabsPegasus@official", + "added_in": "1.11.0" + }, + { + "legacy_slot": "ext:twelvelabs:TwelveLabsPegasus@official-pre-a", + "target": "ext:twelvelabs:TwelveLabsPegasus@official", + "added_in": "1.11.0" + }, + { + "bare_class_name": "TwelveLabsVideoEmbeddingsComponent", + "target": "ext:twelvelabs:TwelveLabsVideoEmbeddingsComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.twelvelabs.video_embeddings.TwelveLabsVideoEmbeddingsComponent", + "target": "ext:twelvelabs:TwelveLabsVideoEmbeddingsComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.twelvelabs.TwelveLabsVideoEmbeddingsComponent", + "target": "ext:twelvelabs:TwelveLabsVideoEmbeddingsComponent@official", + "added_in": "1.11.0" + }, + { + "legacy_slot": "ext:twelvelabs:TwelveLabsVideoEmbeddingsComponent@official-pre-a", + "target": "ext:twelvelabs:TwelveLabsVideoEmbeddingsComponent@official", + "added_in": "1.11.0" + }, + { + "bare_class_name": "VideoFileComponent", + "target": "ext:twelvelabs:VideoFileComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.twelvelabs.video_file.VideoFileComponent", + "target": "ext:twelvelabs:VideoFileComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.twelvelabs.VideoFileComponent", + "target": "ext:twelvelabs:VideoFileComponent@official", + "added_in": "1.11.0" + }, + { + "legacy_slot": "ext:twelvelabs:VideoFileComponent@official-pre-a", + "target": "ext:twelvelabs:VideoFileComponent@official", + "added_in": "1.11.0" + }, + { + "bare_class_name": "JigsawStackAIScraperComponent", + "target": "ext:jigsawstack:JigsawStackAIScraperComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.jigsawstack.ai_scrape.JigsawStackAIScraperComponent", + "target": "ext:jigsawstack:JigsawStackAIScraperComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.jigsawstack.JigsawStackAIScraperComponent", + "target": "ext:jigsawstack:JigsawStackAIScraperComponent@official", + "added_in": "1.11.0" + }, + { + "legacy_slot": "ext:jigsawstack:JigsawStackAIScraperComponent@official-pre-a", + "target": "ext:jigsawstack:JigsawStackAIScraperComponent@official", + "added_in": "1.11.0" + }, + { + "bare_class_name": "JigsawStackAIWebSearchComponent", + "target": "ext:jigsawstack:JigsawStackAIWebSearchComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.jigsawstack.ai_web_search.JigsawStackAIWebSearchComponent", + "target": "ext:jigsawstack:JigsawStackAIWebSearchComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.jigsawstack.JigsawStackAIWebSearchComponent", + "target": "ext:jigsawstack:JigsawStackAIWebSearchComponent@official", + "added_in": "1.11.0" + }, + { + "legacy_slot": "ext:jigsawstack:JigsawStackAIWebSearchComponent@official-pre-a", + "target": "ext:jigsawstack:JigsawStackAIWebSearchComponent@official", + "added_in": "1.11.0" + }, + { + "bare_class_name": "JigsawStackFileReadComponent", + "target": "ext:jigsawstack:JigsawStackFileReadComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.jigsawstack.file_read.JigsawStackFileReadComponent", + "target": "ext:jigsawstack:JigsawStackFileReadComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.jigsawstack.JigsawStackFileReadComponent", + "target": "ext:jigsawstack:JigsawStackFileReadComponent@official", + "added_in": "1.11.0" + }, + { + "legacy_slot": "ext:jigsawstack:JigsawStackFileReadComponent@official-pre-a", + "target": "ext:jigsawstack:JigsawStackFileReadComponent@official", + "added_in": "1.11.0" + }, + { + "bare_class_name": "JigsawStackFileUploadComponent", + "target": "ext:jigsawstack:JigsawStackFileUploadComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.jigsawstack.file_upload.JigsawStackFileUploadComponent", + "target": "ext:jigsawstack:JigsawStackFileUploadComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.jigsawstack.JigsawStackFileUploadComponent", + "target": "ext:jigsawstack:JigsawStackFileUploadComponent@official", + "added_in": "1.11.0" + }, + { + "legacy_slot": "ext:jigsawstack:JigsawStackFileUploadComponent@official-pre-a", + "target": "ext:jigsawstack:JigsawStackFileUploadComponent@official", + "added_in": "1.11.0" + }, + { + "bare_class_name": "JigsawStackImageGenerationComponent", + "target": "ext:jigsawstack:JigsawStackImageGenerationComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.jigsawstack.image_generation.JigsawStackImageGenerationComponent", + "target": "ext:jigsawstack:JigsawStackImageGenerationComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.jigsawstack.JigsawStackImageGenerationComponent", + "target": "ext:jigsawstack:JigsawStackImageGenerationComponent@official", + "added_in": "1.11.0" + }, + { + "legacy_slot": "ext:jigsawstack:JigsawStackImageGenerationComponent@official-pre-a", + "target": "ext:jigsawstack:JigsawStackImageGenerationComponent@official", + "added_in": "1.11.0" + }, + { + "bare_class_name": "JigsawStackNSFWComponent", + "target": "ext:jigsawstack:JigsawStackNSFWComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.jigsawstack.nsfw.JigsawStackNSFWComponent", + "target": "ext:jigsawstack:JigsawStackNSFWComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.jigsawstack.JigsawStackNSFWComponent", + "target": "ext:jigsawstack:JigsawStackNSFWComponent@official", + "added_in": "1.11.0" + }, + { + "legacy_slot": "ext:jigsawstack:JigsawStackNSFWComponent@official-pre-a", + "target": "ext:jigsawstack:JigsawStackNSFWComponent@official", + "added_in": "1.11.0" + }, + { + "bare_class_name": "JigsawStackObjectDetectionComponent", + "target": "ext:jigsawstack:JigsawStackObjectDetectionComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.jigsawstack.object_detection.JigsawStackObjectDetectionComponent", + "target": "ext:jigsawstack:JigsawStackObjectDetectionComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.jigsawstack.JigsawStackObjectDetectionComponent", + "target": "ext:jigsawstack:JigsawStackObjectDetectionComponent@official", + "added_in": "1.11.0" + }, + { + "legacy_slot": "ext:jigsawstack:JigsawStackObjectDetectionComponent@official-pre-a", + "target": "ext:jigsawstack:JigsawStackObjectDetectionComponent@official", + "added_in": "1.11.0" + }, + { + "bare_class_name": "JigsawStackSentimentComponent", + "target": "ext:jigsawstack:JigsawStackSentimentComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.jigsawstack.sentiment.JigsawStackSentimentComponent", + "target": "ext:jigsawstack:JigsawStackSentimentComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.jigsawstack.JigsawStackSentimentComponent", + "target": "ext:jigsawstack:JigsawStackSentimentComponent@official", + "added_in": "1.11.0" + }, + { + "legacy_slot": "ext:jigsawstack:JigsawStackSentimentComponent@official-pre-a", + "target": "ext:jigsawstack:JigsawStackSentimentComponent@official", + "added_in": "1.11.0" + }, + { + "bare_class_name": "JigsawStackTextToSQLComponent", + "target": "ext:jigsawstack:JigsawStackTextToSQLComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.jigsawstack.text_to_sql.JigsawStackTextToSQLComponent", + "target": "ext:jigsawstack:JigsawStackTextToSQLComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.jigsawstack.JigsawStackTextToSQLComponent", + "target": "ext:jigsawstack:JigsawStackTextToSQLComponent@official", + "added_in": "1.11.0" + }, + { + "legacy_slot": "ext:jigsawstack:JigsawStackTextToSQLComponent@official-pre-a", + "target": "ext:jigsawstack:JigsawStackTextToSQLComponent@official", + "added_in": "1.11.0" + }, + { + "bare_class_name": "JigsawStackTextTranslateComponent", + "target": "ext:jigsawstack:JigsawStackTextTranslateComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.jigsawstack.text_translate.JigsawStackTextTranslateComponent", + "target": "ext:jigsawstack:JigsawStackTextTranslateComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.jigsawstack.JigsawStackTextTranslateComponent", + "target": "ext:jigsawstack:JigsawStackTextTranslateComponent@official", + "added_in": "1.11.0" + }, + { + "legacy_slot": "ext:jigsawstack:JigsawStackTextTranslateComponent@official-pre-a", + "target": "ext:jigsawstack:JigsawStackTextTranslateComponent@official", + "added_in": "1.11.0" + }, + { + "bare_class_name": "JigsawStackVOCRComponent", + "target": "ext:jigsawstack:JigsawStackVOCRComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.jigsawstack.vocr.JigsawStackVOCRComponent", + "target": "ext:jigsawstack:JigsawStackVOCRComponent@official", + "added_in": "1.11.0" + }, + { + "import_path": "lfx.components.jigsawstack.JigsawStackVOCRComponent", + "target": "ext:jigsawstack:JigsawStackVOCRComponent@official", + "added_in": "1.11.0" + }, + { + "legacy_slot": "ext:jigsawstack:JigsawStackVOCRComponent@official-pre-a", + "target": "ext:jigsawstack:JigsawStackVOCRComponent@official", + "added_in": "1.11.0" } ], "ambiguous_bare_names": [ diff --git a/uv.lock b/uv.lock index 4a8140affe..c536c42404 100644 --- a/uv.lock +++ b/uv.lock @@ -9142,12 +9142,14 @@ all = [ { name = "assemblyai" }, { name = "atlassian-python-api" }, { name = "chromadb" }, + { name = "cleanlab-tlm" }, { name = "clickhouse-connect" }, { name = "couchbase" }, { name = "firecrawl-py" }, { name = "gitpython" }, { name = "google-api-python-client" }, { name = "google-search-results" }, + { name = "jigsawstack" }, { name = "langchain-chroma" }, { name = "langchain-community" }, { name = "langchain-groq" }, @@ -9177,6 +9179,7 @@ all = [ { name = "spider-client" }, { name = "supabase" }, { name = "toolguard" }, + { name = "twelvelabs" }, { name = "upstash-vector" }, { name = "weaviate-client" }, { name = "wikipedia" }, @@ -9201,6 +9204,9 @@ chroma = [ { name = "langchain-chroma" }, { name = "langchain-community" }, ] +cleanlab = [ + { name = "cleanlab-tlm" }, +] clickhouse = [ { name = "clickhouse-connect" }, { name = "langchain-community" }, @@ -9238,6 +9244,9 @@ groq = [ icosacomputing = [ { name = "requests" }, ] +jigsawstack = [ + { name = "jigsawstack" }, +] litellm = [ { name = "langchain-openai" }, { name = "openai" }, @@ -9309,6 +9318,9 @@ supabase = [ toolguard = [ { name = "toolguard" }, ] +twelvelabs = [ + { name = "twelvelabs" }, +] unstructured = [ { name = "langchain-unstructured" }, ] @@ -9355,12 +9367,14 @@ requires-dist = [ { name = "assemblyai", marker = "extra == 'assemblyai'", specifier = ">=0.33.0,<1.0.0" }, { name = "atlassian-python-api", marker = "extra == 'confluence'", specifier = "==3.41.16" }, { name = "chromadb", marker = "extra == 'chroma'", specifier = ">=1.0.0,<2.0.0" }, + { name = "cleanlab-tlm", marker = "extra == 'cleanlab'", specifier = ">=1.1.2,<2.0.0" }, { name = "clickhouse-connect", marker = "extra == 'clickhouse'", specifier = "==0.7.19" }, { name = "couchbase", marker = "extra == 'couchbase'", specifier = ">=4.2.1" }, { name = "firecrawl-py", marker = "extra == 'firecrawl'", specifier = ">=1.0.16,<2.0.0" }, { name = "gitpython", marker = "extra == 'git'", specifier = ">=3.1.50" }, { name = "google-api-python-client", marker = "extra == 'youtube'", specifier = "~=2.161" }, { name = "google-search-results", marker = "extra == 'serpapi'", specifier = ">=2.4.1,<3.0.0" }, + { name = "jigsawstack", marker = "extra == 'jigsawstack'", specifier = "==0.2.7" }, { name = "langchain-chroma", marker = "extra == 'chroma'", specifier = "~=0.2.6" }, { name = "langchain-community", marker = "extra == 'apify'", specifier = ">=0.4.1,<1.0.0" }, { name = "langchain-community", marker = "extra == 'chroma'", specifier = ">=0.4.1,<1.0.0" }, @@ -9403,6 +9417,7 @@ requires-dist = [ { name = "lfx-bundles", extras = ["assemblyai"], marker = "extra == 'all'", editable = "src/bundles/lfx-bundles" }, { name = "lfx-bundles", extras = ["azure"], marker = "extra == 'all'", editable = "src/bundles/lfx-bundles" }, { name = "lfx-bundles", extras = ["chroma"], marker = "extra == 'all'", editable = "src/bundles/lfx-bundles" }, + { name = "lfx-bundles", extras = ["cleanlab"], marker = "extra == 'all'", editable = "src/bundles/lfx-bundles" }, { name = "lfx-bundles", extras = ["clickhouse"], marker = "extra == 'all'", editable = "src/bundles/lfx-bundles" }, { name = "lfx-bundles", extras = ["cometapi"], marker = "extra == 'all'", editable = "src/bundles/lfx-bundles" }, { name = "lfx-bundles", extras = ["confluence"], marker = "extra == 'all'", editable = "src/bundles/lfx-bundles" }, @@ -9414,6 +9429,7 @@ requires-dist = [ { name = "lfx-bundles", extras = ["glean"], marker = "extra == 'all'", editable = "src/bundles/lfx-bundles" }, { name = "lfx-bundles", extras = ["groq"], marker = "extra == 'all'", editable = "src/bundles/lfx-bundles" }, { name = "lfx-bundles", extras = ["icosacomputing"], marker = "extra == 'all'", editable = "src/bundles/lfx-bundles" }, + { name = "lfx-bundles", extras = ["jigsawstack"], marker = "extra == 'all'", editable = "src/bundles/lfx-bundles" }, { name = "lfx-bundles", extras = ["litellm"], marker = "extra == 'all'", editable = "src/bundles/lfx-bundles" }, { name = "lfx-bundles", extras = ["lmstudio"], marker = "extra == 'all'", editable = "src/bundles/lfx-bundles" }, { name = "lfx-bundles", extras = ["mem0"], marker = "extra == 'all'", editable = "src/bundles/lfx-bundles" }, @@ -9435,6 +9451,7 @@ requires-dist = [ { name = "lfx-bundles", extras = ["supabase"], marker = "extra == 'all'", editable = "src/bundles/lfx-bundles" }, { name = "lfx-bundles", extras = ["tavily"], marker = "extra == 'all'", editable = "src/bundles/lfx-bundles" }, { name = "lfx-bundles", extras = ["toolguard"], marker = "extra == 'all'", editable = "src/bundles/lfx-bundles" }, + { name = "lfx-bundles", extras = ["twelvelabs"], marker = "extra == 'all'", editable = "src/bundles/lfx-bundles" }, { name = "lfx-bundles", extras = ["unstructured"], marker = "extra == 'all'", editable = "src/bundles/lfx-bundles" }, { name = "lfx-bundles", extras = ["upstash"], marker = "extra == 'all'", editable = "src/bundles/lfx-bundles" }, { name = "lfx-bundles", extras = ["vllm"], marker = "extra == 'all'", editable = "src/bundles/lfx-bundles" }, @@ -9467,6 +9484,7 @@ requires-dist = [ { name = "spider-client", marker = "extra == 'spider'", specifier = ">=0.0.27,<1.0.0" }, { name = "supabase", marker = "extra == 'supabase'", specifier = ">=2.6.0,<3.0.0" }, { name = "toolguard", marker = "extra == 'toolguard'", specifier = ">=0.2.16,<1.0.0" }, + { name = "twelvelabs", marker = "extra == 'twelvelabs'", specifier = ">=0.4.7,<1.0.0" }, { name = "upstash-vector", marker = "extra == 'upstash'", specifier = "==0.6.0" }, { name = "weaviate-client", marker = "extra == 'weaviate'", specifier = ">=4.10.2,<5.0.0" }, { name = "wikipedia", marker = "extra == 'wikipedia'", specifier = "==1.4.0" }, @@ -9475,7 +9493,7 @@ requires-dist = [ { name = "youtube-transcript-api", marker = "extra == 'youtube'", specifier = ">=1.0.0,<2.0.0" }, { name = "zep-python", marker = "extra == 'zep'", specifier = "==2.0.2" }, ] -provides-extras = ["aiml", "apify", "assemblyai", "azure", "chroma", "clickhouse", "cometapi", "confluence", "couchbase", "deepseek", "exa", "firecrawl", "git", "glean", "groq", "icosacomputing", "litellm", "lmstudio", "mem0", "milvus", "mistral", "mongodb", "needle", "novita", "ollama", "openrouter", "perplexity", "pgvector", "pinecone", "qdrant", "sambanova", "scrapegraph", "serpapi", "spider", "supabase", "tavily", "toolguard", "unstructured", "upstash", "vllm", "weaviate", "wikipedia", "wolframalpha", "xai", "yahoosearch", "youtube", "zep", "all"] +provides-extras = ["aiml", "apify", "assemblyai", "azure", "chroma", "cleanlab", "clickhouse", "cometapi", "confluence", "couchbase", "deepseek", "exa", "firecrawl", "git", "glean", "groq", "icosacomputing", "jigsawstack", "litellm", "lmstudio", "mem0", "milvus", "mistral", "mongodb", "needle", "novita", "ollama", "openrouter", "perplexity", "pgvector", "pinecone", "qdrant", "sambanova", "scrapegraph", "serpapi", "spider", "supabase", "tavily", "toolguard", "twelvelabs", "unstructured", "upstash", "vllm", "weaviate", "wikipedia", "wolframalpha", "xai", "yahoosearch", "youtube", "zep", "all"] [[package]] name = "lfx-cohere"