fix: add fail safe mode for embeddings model#10726
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughAdds a fail-safe toggle to EmbeddingModelComponent that, when enabled, logs provider errors and returns None instead of raising. Adds async model discovery for Ollama, updates build/update flows to honor fail-safe across OpenAI, Ollama, and IBM watsonx.ai, and adds ingestion/search guard rails and logging in OpenSearch multimodal vector store. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant EmbeddingComp as EmbeddingModelComponent
participant Provider as Provider (OpenAI / Ollama / IBM)
participant Logger
User->>EmbeddingComp: build_embeddings(provider, fail_safe_mode=bool)
EmbeddingComp->>EmbeddingComp: detect provider & prepare config
EmbeddingComp->>Provider: attempt provider/client initialization
alt provider init success
Provider-->>EmbeddingComp: embeddings instance(s)
EmbeddingComp-->>User: return embeddings
else provider init failure
Provider--xEmbeddingComp: error
EmbeddingComp->>Logger: log error (includes diagnostics)
alt fail_safe_mode == true
EmbeddingComp-->>User: return None (no raise)
else
EmbeddingComp-->>User: raise exception
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touchesImportant Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 error, 2 warnings, 1 inconclusive)
✅ Passed checks (3 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Introduces a 'fail_safe_mode' option to the EmbeddingModel component, allowing errors to be logged and None returned instead of raising exceptions. Updates the build_embeddings and update_build_config logic to support this mode for OpenAI, Ollama, and IBM watsonx.ai providers.
…/langflow into fail-safe-embeddings
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (4)
src/backend/base/langflow/initial_setup/starter_projects/Nvidia Remix.json (1)
2038-2058: Fail‑safe toggle addition is good; add UX note.Nice addition. Consider augmenting the info/help text to hint that downstream components (e.g., vector stores) may still require a valid Embeddings instance, so flows may no‑op or surface tool‑level errors when None is returned.
src/lfx/src/lfx/components/elastic/opensearch_multimodal.py (1)
1196-1227: Clarify the fail-safe check placement.The check at line 1196 validates that
self.embeddingis truthy and raises if not. The subsequent check at lines 1200-1204 handles the edge case whereself.embeddingis a list containing onlyNonevalues (from fail-safe mode). Consider consolidating or reordering for clarity.if not self.embedding: msg = "Embedding is required to run hybrid search (KNN + keyword)." raise ValueError(msg) - # Check if embedding is None (fail-safe mode) - if self.embedding is None or (isinstance(self.embedding, list) and all(e is None for e in self.embedding)): - logger.error("Embedding returned None (fail-safe mode enabled). Cannot perform search.") - return [] - # Build filter clauses first so we can use them in model detection filter_clauses = self._coerce_filter_clauses(filter_obj) ... # Normalize embedding to list embeddings_list = self.embedding if isinstance(self.embedding, list) else [self.embedding] # Filter out None values (fail-safe mode) embeddings_list = [e for e in embeddings_list if e is not None] if not embeddings_list: - logger.error( - "No valid embeddings available after filtering None values (fail-safe mode). Cannot perform search." - ) + logger.error("All embeddings returned None (fail-safe mode). Cannot perform search.") return []This removes the redundant early check since the filtering at lines 1220-1227 already handles all scenarios.
src/lfx/src/lfx/components/models_and_agents/embedding_model.py (2)
213-231: Consider lazy instantiation for available models.Creating embedding instances for all OpenAI models upfront (currently iterating through
OPENAI_EMBEDDING_MODEL_NAMES) is potentially inefficient, especially if only one model is used. Additionally, applying the samedimensionsconfig to all models may cause issues since different models have different dimension constraints (e.g.,text-embedding-3-smallmax 1536 vstext-embedding-3-largemax 3072).Consider lazy instantiation or at minimum, handle model-specific dimension constraints:
# Create dedicated instances for each available model available_models_dict = {} for model_name in OPENAI_EMBEDDING_MODEL_NAMES: + # Note: dimensions may need model-specific handling available_models_dict[model_name] = OpenAIEmbeddings( model=model_name, - dimensions=dimensions or None, # Use same dimensions config for all + dimensions=None, # Let each model use its default dimensions base_url=api_base or None, api_key=api_key, chunk_size=chunk_size, max_retries=max_retries, timeout=request_timeout or None, show_progress_bar=show_progress_bar, model_kwargs=model_kwargs, )Alternatively, implement lazy instantiation to only create instances when actually needed.
432-434: Avoid duplicate API calls for IBM models.
fetch_ibm_modelsis called twice with the same argument, resulting in redundant HTTP requests.elif field_value == "IBM watsonx.ai": - build_config["model"]["options"] = self.fetch_ibm_models(base_url=self.base_url_ibm_watsonx) - build_config["model"]["value"] = self.fetch_ibm_models(base_url=self.base_url_ibm_watsonx)[0] + ibm_models = self.fetch_ibm_models(base_url=self.base_url_ibm_watsonx) + build_config["model"]["options"] = ibm_models + build_config["model"]["value"] = ibm_models[0] if ibm_models else ""The same issue exists at lines 446-447.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/backend/base/langflow/initial_setup/starter_projects/Nvidia Remix.json(3 hunks)src/lfx/src/lfx/components/elastic/opensearch_multimodal.py(8 hunks)src/lfx/src/lfx/components/models_and_agents/embedding_model.py(9 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/lfx/src/lfx/components/models_and_agents/embedding_model.py (3)
src/lfx/src/lfx/base/models/model_utils.py (1)
get_ollama_models(39-108)src/lfx/src/lfx/base/embeddings/embeddings_class.py (1)
EmbeddingsWithModels(6-113)src/lfx/src/lfx/utils/util.py (1)
transform_localhost_url(119-161)
src/lfx/src/lfx/components/elastic/opensearch_multimodal.py (1)
src/lfx/src/lfx/custom/custom_component/component.py (1)
log(1523-1540)
🪛 GitHub Actions: Ruff Style Check
src/lfx/src/lfx/components/elastic/opensearch_multimodal.py
[error] 674-674: Ruff: E501 Line too long (129 > 120).
🪛 GitHub Check: Ruff Style Check (3.13)
src/lfx/src/lfx/components/elastic/opensearch_multimodal.py
[failure] 677-677: Ruff (E501)
src/lfx/src/lfx/components/elastic/opensearch_multimodal.py:677:121: E501 Line too long (133 > 120)
[failure] 674-674: Ruff (E501)
src/lfx/src/lfx/components/elastic/opensearch_multimodal.py:674:121: E501 Line too long (129 > 120)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (52)
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 21/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 18/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 29/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 37/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 36/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 40/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 38/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 34/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 35/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 26/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 33/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 30/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 28/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 39/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 32/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 24/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 25/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 31/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 23/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 27/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 22/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 20/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 19/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 8/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 13/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 17/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 9/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 16/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 14/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 15/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 12/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 7/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 11/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 5/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 10/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 6/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 2/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 1/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 3/40
- GitHub Check: Run Frontend Tests / Playwright Tests - Shard 4/40
- GitHub Check: Test Docker Images / Test docker images
- GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 5
- GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 4
- GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 2
- GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 3
- GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 1
- GitHub Check: Run Backend Tests / Integration Tests - Python 3.10
- GitHub Check: Run Backend Tests / LFX Tests - Python 3.10
- GitHub Check: Test Starter Templates
- GitHub Check: Optimize new Python code in this PR
- GitHub Check: test-starter-projects
- GitHub Check: Update Component Index
🔇 Additional comments (8)
src/backend/base/langflow/initial_setup/starter_projects/Nvidia Remix.json (1)
1856-1856: Metadata code_hash bump looks correct.No functional risk; consistent with updating embedded component code.
src/lfx/src/lfx/components/elastic/opensearch_multimodal.py (2)
689-701: LGTM!Good defensive programming: normalizing to list, filtering None values, and gracefully handling the case when all embeddings are None (fail-safe mode). The early return with logging prevents downstream errors.
1553-1584: LGTM!The refactored
search_documentscorrectly separates ingestion from search: it always ensures the vector store is built (triggering any pending ingestion), then conditionally performs search only when a query is provided. The explicit log message clarifies the behavior in ingestion-only mode.src/lfx/src/lfx/components/models_and_agents/embedding_model.py (5)
135-143: LGTM!The
fail_safe_modeinput is well-defined with clear documentation explaining that errors will be logged and the component may returnNone. Thereal_time_refresh=Truesetting correctly triggers UI updates when toggled.
164-175: LGTM!Good encapsulation of the Ollama model discovery logic. The async method correctly wraps
get_ollama_modelsand returns an empty list on failure, consistent with the fail-safe approach.
252-296: LGTM!Good defensive handling of the Ollama URL, including automatic removal of the
/v1suffix with a helpful warning message directing users to the correct component for OpenAI-compatible API. The fail-safe wrapping correctly catches both sync and async errors.
324-371: LGTM!Good design sharing the
api_clientinstance across all model embeddings, which is efficient for connection pooling. The fail-safe handling correctly captures authentication and initialization errors.
382-391: LGTM!Correct handling of
fail_safe_modetoggle: when enabled, the API key is no longer required, allowing the component to gracefully handle missing credentials. When disabled, the required flag is restored based on the current provider.
| "value": "from typing import Any\n\nimport requests\nfrom ibm_watsonx_ai.metanames import EmbedTextParamsMetaNames\nfrom langchain_openai import OpenAIEmbeddings\n\nfrom lfx.base.embeddings.embeddings_class import EmbeddingsWithModels\nfrom lfx.base.embeddings.model import LCEmbeddingsModel\nfrom lfx.base.models.model_utils import get_ollama_models, is_valid_ollama_url\nfrom lfx.base.models.openai_constants import OPENAI_EMBEDDING_MODEL_NAMES\nfrom lfx.base.models.watsonx_constants import (\n IBM_WATSONX_URLS,\n WATSONX_EMBEDDING_MODEL_NAMES,\n)\nfrom lfx.field_typing import Embeddings\nfrom lfx.io import (\n BoolInput,\n DictInput,\n DropdownInput,\n FloatInput,\n IntInput,\n MessageTextInput,\n SecretStrInput,\n)\nfrom lfx.log.logger import logger\nfrom lfx.schema.dotdict import dotdict\nfrom lfx.utils.util import transform_localhost_url\n\n# Ollama API constants\nHTTP_STATUS_OK = 200\nJSON_MODELS_KEY = \"models\"\nJSON_NAME_KEY = \"name\"\nJSON_CAPABILITIES_KEY = \"capabilities\"\nDESIRED_CAPABILITY = \"embedding\"\nDEFAULT_OLLAMA_URL = \"http://localhost:11434\"\n\n\nclass EmbeddingModelComponent(LCEmbeddingsModel):\n display_name = \"Embedding Model\"\n description = \"Generate embeddings using a specified provider.\"\n documentation: str = \"https://docs.langflow.org/components-embedding-models\"\n icon = \"binary\"\n name = \"EmbeddingModel\"\n category = \"models\"\n\n inputs = [\n DropdownInput(\n name=\"provider\",\n display_name=\"Model Provider\",\n options=[\"OpenAI\", \"Ollama\", \"IBM watsonx.ai\"],\n value=\"OpenAI\",\n info=\"Select the embedding model provider\",\n real_time_refresh=True,\n options_metadata=[{\"icon\": \"OpenAI\"}, {\"icon\": \"Ollama\"}, {\"icon\": \"WatsonxAI\"}],\n ),\n MessageTextInput(\n name=\"api_base\",\n display_name=\"API Base URL\",\n info=\"Base URL for the API. Leave empty for default.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"ollama_base_url\",\n display_name=\"Ollama API URL\",\n info=f\"Endpoint of the Ollama API (Ollama only). Defaults to {DEFAULT_OLLAMA_URL}\",\n value=DEFAULT_OLLAMA_URL,\n show=False,\n real_time_refresh=True,\n load_from_db=True,\n ),\n DropdownInput(\n name=\"base_url_ibm_watsonx\",\n display_name=\"watsonx API Endpoint\",\n info=\"The base URL of the API (IBM watsonx.ai only)\",\n options=IBM_WATSONX_URLS,\n value=IBM_WATSONX_URLS[0],\n show=False,\n real_time_refresh=True,\n ),\n DropdownInput(\n name=\"model\",\n display_name=\"Model Name\",\n options=OPENAI_EMBEDDING_MODEL_NAMES,\n value=OPENAI_EMBEDDING_MODEL_NAMES[0],\n info=\"Select the embedding model to use\",\n real_time_refresh=True,\n refresh_button=True,\n ),\n SecretStrInput(\n name=\"api_key\",\n display_name=\"OpenAI API Key\",\n info=\"Model Provider API key\",\n required=True,\n show=True,\n real_time_refresh=True,\n ),\n # Watson-specific inputs\n MessageTextInput(\n name=\"project_id\",\n display_name=\"Project ID\",\n info=\"IBM watsonx.ai Project ID (required for IBM watsonx.ai)\",\n show=False,\n ),\n IntInput(\n name=\"dimensions\",\n display_name=\"Dimensions\",\n info=\"The number of dimensions the resulting output embeddings should have. \"\n \"Only supported by certain models.\",\n advanced=True,\n ),\n IntInput(name=\"chunk_size\", display_name=\"Chunk Size\", advanced=True, value=1000),\n FloatInput(name=\"request_timeout\", display_name=\"Request Timeout\", advanced=True),\n IntInput(name=\"max_retries\", display_name=\"Max Retries\", advanced=True, value=3),\n BoolInput(name=\"show_progress_bar\", display_name=\"Show Progress Bar\", advanced=True),\n DictInput(\n name=\"model_kwargs\",\n display_name=\"Model Kwargs\",\n advanced=True,\n info=\"Additional keyword arguments to pass to the model.\",\n ),\n IntInput(\n name=\"truncate_input_tokens\",\n display_name=\"Truncate Input Tokens\",\n advanced=True,\n value=200,\n show=False,\n ),\n BoolInput(\n name=\"input_text\",\n display_name=\"Include the original text in the output\",\n value=True,\n advanced=True,\n show=False,\n ),\n BoolInput(\n name=\"fail_safe_mode\",\n display_name=\"Fail-Safe Mode\",\n value=False,\n advanced=True,\n info=\"When enabled, errors will be logged instead of raising exceptions. \"\n \"The component will return None on error.\",\n real_time_refresh=True,\n ),\n ]\n\n @staticmethod\n def fetch_ibm_models(base_url: str) -> list[str]:\n \"\"\"Fetch available models from the watsonx.ai API.\"\"\"\n try:\n endpoint = f\"{base_url}/ml/v1/foundation_model_specs\"\n params = {\n \"version\": \"2024-09-16\",\n \"filters\": \"function_embedding,!lifecycle_withdrawn:and\",\n }\n response = requests.get(endpoint, params=params, timeout=10)\n response.raise_for_status()\n data = response.json()\n models = [model[\"model_id\"] for model in data.get(\"resources\", [])]\n return sorted(models)\n except Exception: # noqa: BLE001\n logger.exception(\"Error fetching models\")\n return WATSONX_EMBEDDING_MODEL_NAMES\n async def fetch_ollama_models(self) -> list[str]:\n try:\n return await get_ollama_models(\n base_url_value=self.ollama_base_url,\n desired_capability=DESIRED_CAPABILITY,\n json_models_key=JSON_MODELS_KEY,\n json_name_key=JSON_NAME_KEY,\n json_capabilities_key=JSON_CAPABILITIES_KEY,\n )\n except Exception: # noqa: BLE001\n\n logger.exception(\"Error fetching models\")\n return []\n async def build_embeddings(self) -> Embeddings:\n provider = self.provider\n model = self.model\n api_key = self.api_key\n api_base = self.api_base\n base_url_ibm_watsonx = self.base_url_ibm_watsonx\n ollama_base_url = self.ollama_base_url\n dimensions = self.dimensions\n chunk_size = self.chunk_size\n request_timeout = self.request_timeout\n max_retries = self.max_retries\n show_progress_bar = self.show_progress_bar\n model_kwargs = self.model_kwargs or {}\n\n if provider == \"OpenAI\":\n if not api_key:\n msg = \"OpenAI API key is required when using OpenAI provider\"\n if self.fail_safe_mode:\n logger.error(msg)\n return None\n raise ValueError(msg)\n\n try:\n # Create the primary embedding instance\n embeddings_instance = OpenAIEmbeddings(\n model=model,\n dimensions=dimensions or None,\n base_url=api_base or None,\n api_key=api_key,\n chunk_size=chunk_size,\n max_retries=max_retries,\n timeout=request_timeout or None,\n show_progress_bar=show_progress_bar,\n model_kwargs=model_kwargs,\n )\n\n # Create dedicated instances for each available model\n available_models_dict = {}\n for model_name in OPENAI_EMBEDDING_MODEL_NAMES:\n available_models_dict[model_name] = OpenAIEmbeddings(\n model=model_name,\n dimensions=dimensions or None, # Use same dimensions config for all\n base_url=api_base or None,\n api_key=api_key,\n chunk_size=chunk_size,\n max_retries=max_retries,\n timeout=request_timeout or None,\n show_progress_bar=show_progress_bar,\n model_kwargs=model_kwargs,\n )\n\n return EmbeddingsWithModels(\n embeddings=embeddings_instance,\n available_models=available_models_dict,\n )\n except Exception as e:\n msg = f\"Failed to initialize OpenAI embeddings: {e}\"\n if self.fail_safe_mode:\n logger.error(msg)\n return None\n raise\n\n if provider == \"Ollama\":\n try:\n from langchain_ollama import OllamaEmbeddings\n except ImportError:\n try:\n from langchain_community.embeddings import OllamaEmbeddings\n except ImportError:\n msg = \"Please install langchain-ollama: pip install langchain-ollama\"\n if self.fail_safe_mode:\n logger.error(msg)\n return None\n raise ImportError(msg) from None\n\n try:\n transformed_base_url = transform_localhost_url(ollama_base_url)\n\n # Check if URL contains /v1 suffix (OpenAI-compatible mode)\n if transformed_base_url and transformed_base_url.rstrip(\"/\").endswith(\"/v1\"):\n # Strip /v1 suffix and log warning\n transformed_base_url = transformed_base_url.rstrip(\"/\").removesuffix(\"/v1\")\n logger.warning(\n \"Detected '/v1' suffix in base URL. The Ollama component uses the native Ollama API, \"\n \"not the OpenAI-compatible API. The '/v1' suffix has been automatically removed. \"\n \"If you want to use the OpenAI-compatible API, please use the OpenAI component instead. \"\n \"Learn more at https://docs.ollama.com/openai#openai-compatibility\"\n )\n\n final_base_url = transformed_base_url or \"http://localhost:11434\"\n\n # Create the primary embedding instance\n embeddings_instance = OllamaEmbeddings(\n model=model,\n base_url=final_base_url,\n **model_kwargs,\n )\n\n # Fetch available Ollama models\n available_model_names = await self.fetch_ollama_models()\n\n # Create dedicated instances for each available model\n available_models_dict = {}\n for model_name in available_model_names:\n available_models_dict[model_name] = OllamaEmbeddings(\n model=model_name,\n base_url=final_base_url,\n **model_kwargs,\n )\n\n return EmbeddingsWithModels(\n embeddings=embeddings_instance,\n available_models=available_models_dict,\n )\n except Exception as e:\n msg = f\"Failed to initialize Ollama embeddings: {e}\"\n if self.fail_safe_mode:\n logger.error(msg)\n return None\n raise\n\n if provider == \"IBM watsonx.ai\":\n try:\n from langchain_ibm import WatsonxEmbeddings\n except ImportError:\n msg = \"Please install langchain-ibm: pip install langchain-ibm\"\n if self.fail_safe_mode:\n logger.error(msg)\n return None\n raise ImportError(msg) from None\n\n if not api_key:\n msg = \"IBM watsonx.ai API key is required when using IBM watsonx.ai provider\"\n if self.fail_safe_mode:\n logger.error(msg)\n return None\n raise ValueError(msg)\n\n project_id = self.project_id\n\n if not project_id:\n msg = \"Project ID is required for IBM watsonx.ai provider\"\n if self.fail_safe_mode:\n logger.error(msg)\n return None\n raise ValueError(msg)\n\n try:\n from ibm_watsonx_ai import APIClient, Credentials\n\n final_url = base_url_ibm_watsonx or \"https://us-south.ml.cloud.ibm.com\"\n\n credentials = Credentials(\n api_key=self.api_key,\n url=final_url,\n )\n\n api_client = APIClient(credentials)\n\n params = {\n EmbedTextParamsMetaNames.TRUNCATE_INPUT_TOKENS: self.truncate_input_tokens,\n EmbedTextParamsMetaNames.RETURN_OPTIONS: {\"input_text\": self.input_text},\n }\n\n # Create the primary embedding instance\n embeddings_instance = WatsonxEmbeddings(\n model_id=model,\n params=params,\n watsonx_client=api_client,\n project_id=project_id,\n )\n\n # Fetch available IBM watsonx.ai models\n available_model_names = self.fetch_ibm_models(final_url)\n\n # Create dedicated instances for each available model\n available_models_dict = {}\n for model_name in available_model_names:\n available_models_dict[model_name] = WatsonxEmbeddings(\n model_id=model_name,\n params=params,\n watsonx_client=api_client,\n project_id=project_id,\n )\n\n return EmbeddingsWithModels(\n embeddings=embeddings_instance,\n available_models=available_models_dict,\n )\n except Exception as e:\n msg = f\"Failed to authenticate with IBM watsonx.ai: {e}\"\n if self.fail_safe_mode:\n logger.error(msg)\n return None\n raise\n\n msg = f\"Unknown provider: {provider}\"\n if self.fail_safe_mode:\n logger.error(msg)\n return None\n raise ValueError(msg)\n\n async def update_build_config(\n self, build_config: dotdict, field_value: Any, field_name: str | None = None\n ) -> dotdict:\n # Handle fail_safe_mode changes first - set all required fields to False if enabled\n if field_name == \"fail_safe_mode\":\n if field_value: # If fail_safe_mode is enabled\n build_config[\"api_key\"][\"required\"] = False\n elif hasattr(self, \"provider\"):\n # If fail_safe_mode is disabled, restore required flags based on provider\n if self.provider in [\"OpenAI\", \"IBM watsonx.ai\"]:\n build_config[\"api_key\"][\"required\"] = True\n else: # Ollama\n build_config[\"api_key\"][\"required\"] = False\n\n if field_name == \"provider\":\n if field_value == \"OpenAI\":\n build_config[\"model\"][\"options\"] = OPENAI_EMBEDDING_MODEL_NAMES\n build_config[\"model\"][\"value\"] = OPENAI_EMBEDDING_MODEL_NAMES[0]\n build_config[\"api_key\"][\"display_name\"] = \"OpenAI API Key\"\n # Only set required=True if fail_safe_mode is not enabled\n build_config[\"api_key\"][\"required\"] = not (hasattr(self, \"fail_safe_mode\") and self.fail_safe_mode)\n build_config[\"api_key\"][\"show\"] = True\n build_config[\"api_base\"][\"display_name\"] = \"OpenAI API Base URL\"\n build_config[\"api_base\"][\"advanced\"] = True\n build_config[\"api_base\"][\"show\"] = True\n build_config[\"ollama_base_url\"][\"show\"] = False\n build_config[\"project_id\"][\"show\"] = False\n build_config[\"base_url_ibm_watsonx\"][\"show\"] = False\n build_config[\"truncate_input_tokens\"][\"show\"] = False\n build_config[\"input_text\"][\"show\"] = False\n elif field_value == \"Ollama\":\n build_config[\"ollama_base_url\"][\"show\"] = True\n\n if await is_valid_ollama_url(url=self.ollama_base_url):\n try:\n models = await self.fetch_ollama_models()\n build_config[\"model\"][\"options\"] = models\n build_config[\"model\"][\"value\"] = models[0] if models else \"\"\n except ValueError:\n build_config[\"model\"][\"options\"] = []\n build_config[\"model\"][\"value\"] = \"\"\n else:\n build_config[\"model\"][\"options\"] = []\n build_config[\"model\"][\"value\"] = \"\"\n build_config[\"truncate_input_tokens\"][\"show\"] = False\n build_config[\"input_text\"][\"show\"] = False\n build_config[\"api_key\"][\"display_name\"] = \"API Key (Optional)\"\n build_config[\"api_key\"][\"required\"] = False\n build_config[\"api_key\"][\"show\"] = False\n build_config[\"api_base\"][\"show\"] = False\n build_config[\"project_id\"][\"show\"] = False\n build_config[\"base_url_ibm_watsonx\"][\"show\"] = False\n\n elif field_value == \"IBM watsonx.ai\":\n build_config[\"model\"][\"options\"] = self.fetch_ibm_models(base_url=self.base_url_ibm_watsonx)\n build_config[\"model\"][\"value\"] = self.fetch_ibm_models(base_url=self.base_url_ibm_watsonx)[0]\n build_config[\"api_key\"][\"display_name\"] = \"IBM watsonx.ai API Key\"\n # Only set required=True if fail_safe_mode is not enabled\n build_config[\"api_key\"][\"required\"] = not (hasattr(self, \"fail_safe_mode\") and self.fail_safe_mode)\n build_config[\"api_key\"][\"show\"] = True\n build_config[\"api_base\"][\"show\"] = False\n build_config[\"ollama_base_url\"][\"show\"] = False\n build_config[\"base_url_ibm_watsonx\"][\"show\"] = True\n build_config[\"project_id\"][\"show\"] = True\n build_config[\"truncate_input_tokens\"][\"show\"] = True\n build_config[\"input_text\"][\"show\"] = True\n elif field_name == \"base_url_ibm_watsonx\":\n build_config[\"model\"][\"options\"] = self.fetch_ibm_models(base_url=field_value)\n build_config[\"model\"][\"value\"] = self.fetch_ibm_models(base_url=field_value)[0]\n elif field_name == \"ollama_base_url\":\n # # Refresh Ollama models when base URL changes\n # if hasattr(self, \"provider\") and self.provider == \"Ollama\":\n # Use field_value if provided, otherwise fall back to instance attribute\n ollama_url = self.ollama_base_url\n if await is_valid_ollama_url(url=ollama_url):\n try:\n models = await self.fetch_ollama_models()\n build_config[\"model\"][\"options\"] = models\n build_config[\"model\"][\"value\"] = models[0] if models else \"\"\n except ValueError:\n await logger.awarning(\"Failed to fetch Ollama embedding models.\")\n build_config[\"model\"][\"options\"] = []\n build_config[\"model\"][\"value\"] = \"\"\n\n elif field_name == \"model\" and self.provider == \"Ollama\":\n ollama_url = self.ollama_base_url\n if await is_valid_ollama_url(url=ollama_url):\n try:\n models = await self.fetch_ollama_models()\n build_config[\"model\"][\"options\"] = models\n except ValueError:\n await logger.awarning(\"Failed to refresh Ollama embedding models.\")\n build_config[\"model\"][\"options\"] = []\n\n return build_config\n" | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
Script executed:
#!/bin/bash
# Find the JSON file mentioned in the review
find . -type f -name "Nvidia Remix.json" 2>/dev/null | head -5Repository: langflow-ai/langflow
<shell_result>
Repository: langflow-ai/langflow
Exit code: 0
stdout:
./src/backend/base/langflow/initial_setup/starter_projects/Nvidia Remix.json
./src/lfx/tests/data/starter_projects_1_6_0/Nvidia Remix.json
</shell_result>
Script executed:
#!/bin/bash
# Get line count and examine structure
wc -l "./src/backend/base/langflow/initial_setup/starter_projects/Nvidia Remix.json"
# Extract lines around 2018-2019 to understand context
sed -n '2010,2030p' "./src/backend/base/langflow/initial_setup/starter_projects/Nvidia Remix.json" | head -20Repository: langflow-ai/langflow
<shell_result>
Repository: langflow-ai/langflow
Exit code: 0
stdout:
5278 ./src/backend/base/langflow/initial_setup/starter_projects/Nvidia Remix.json
"multiline": true,
"name": "code",
"password": false,
"placeholder": "",
"required": true,
"show": true,
"title_case": false,
"type": "code",
"value": "from typing import Any\n\nimport requests\nfrom ibm_watsonx_ai.metanames import EmbedTextParamsMetaNames\nfrom langchain_openai import OpenAIEmbeddings\n\nfrom lfx.base.embeddings.embeddings_class import EmbeddingsWithModels\nfrom lfx.base.embeddings.model import LCEmbeddingsModel\nfrom lfx.base.models.model_utils import get_ollama_models, is_valid_ollama_url\nfrom lfx.base.models.openai_constants import OPENAI_EMBEDDING_MODEL_NAMES\nfrom lfx.base.models.watsonx_constants import (\n IBM_WATSONX_URLS,\n WATSONX_EMBEDDING_MODEL_NAMES,\n)\nfrom lfx.field_typing import Embeddings\nfrom lfx.io import (\n BoolInput,\n DictInput,\n DropdownInput,\n FloatInput,\n IntInput,\n MessageTextInput,\n SecretStrInput,\n)\nfrom lfx.log.logger import logger\nfrom lfx.schema.dotdict import dotdict\nfrom lfx.utils.util import transform_localhost_url\n\n# Ollama API constants\nHTTP_STATUS_OK = 200\nJSON_MODELS_KEY = \"models\"\nJSON_NAME_KEY = \"name\"\nJSON_CAPABILITIES_KEY = \"capabilities\"\nDESIRED_CAPABILITY = \"embedding\"\nDEFAULT_OLLAMA_URL = \"http://localhost:11434\"\n\n\nclass EmbeddingModelComponent(LCEmbeddingsModel):\n display_name = \"Embedding Model\"\n description = \"Generate embeddings using a specified provider.\"\n documentation: str = \"https://docs.langflow.org/components-embedding-models\"\n icon = \"binary\"\n name = \"EmbeddingModel\"\n category = \"models\"\n\n inputs = [\n DropdownInput(\n name=\"provider\",\n display_name=\"Model Provider\",\n options=[\"OpenAI\", \"Ollama\", \"IBM watsonx.ai\"],\n value=\"OpenAI\",\n info=\"Select the embedding model provider\",\n real_time_refresh=True,\n options_metadata=[{\"icon\": \"OpenAI\"}, {\"icon\": \"Ollama\"}, {\"icon\": \"WatsonxAI\"}],\n ),\n MessageTextInput(\n name=\"api_base\",\n display_name=\"API Base URL\",\n info=\"Base URL for the API. Leave empty for default.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"ollama_base_url\",\n display_name=\"Ollama API URL\",\n info=f\"Endpoint of the Ollama API (Ollama only). Defaults to {DEFAULT_OLLAMA_URL}\",\n value=DEFAULT_OLLAMA_URL,\n show=False,\n real_time_refresh=True,\n load_from_db=True,\n ),\n DropdownInput(\n name=\"base_url_ibm_watsonx\",\n display_name=\"watsonx API Endpoint\",\n info=\"The base URL of the API (IBM watsonx.ai only)\",\n options=IBM_WATSONX_URLS,\n value=IBM_WATSONX_URLS[0],\n show=False,\n real_time_refresh=True,\n ),\n DropdownInput(\n name=\"model\",\n display_name=\"Model Name\",\n options=OPENAI_EMBEDDING_MODEL_NAMES,\n value=OPENAI_EMBEDDING_MODEL_NAMES[0],\n info=\"Select the embedding model to use\",\n real_time_refresh=True,\n refresh_button=True,\n ),\n SecretStrInput(\n name=\"api_key\",\n display_name=\"OpenAI API Key\",\n info=\"Model Provider API key\",\n required=True,\n show=True,\n real_time_refresh=True,\n ),\n # Watson-specific inputs\n MessageTextInput(\n name=\"project_id\",\n display_name=\"Project ID\",\n info=\"IBM watsonx.ai Project ID (required for IBM watsonx.ai)\",\n show=False,\n ),\n IntInput(\n name=\"dimensions\",\n display_name=\"Dimensions\",\n info=\"The number of dimensions the resulting output embeddings should have. \"\n \"Only supported by certain models.\",\n advanced=True,\n ),\n IntInput(name=\"chunk_size\", display_name=\"Chunk Size\", advanced=True, value=1000),\n FloatInput(name=\"request_timeout\", display_name=\"Request Timeout\", advanced=True),\n IntInput(name=\"max_retries\", display_name=\"Max Retries\", advanced=True, value=3),\n BoolInput(name=\"show_progress_bar\", display_name=\"Show Progress Bar\", advanced=True),\n DictInput(\n name=\"model_kwargs\",\n display_name=\"Model Kwargs\",\n advanced=True,\n info=\"Additional keyword arguments to pass to the model.\",\n ),\n IntInput(\n name=\"truncate_input_tokens\",\n display_name=\"Truncate Input Tokens\",\n advanced=True,\n value=200,\n show=False,\n ),\n BoolInput(\n name=\"input_text\",\n display_name=\"Include the original text in the output\",\n value=True,\n advanced=True,\n show=False,\n ),\n BoolInput(\n name=\"fail_safe_mode\",\n display_name=\"Fail-Safe Mode\",\n value=False,\n advanced=True,\n info=\"When enabled, errors will be logged instead of raising exceptions. \"\n \"The component will return None on error.\",\n real_time_refresh=True,\n ),\n ]\n\n @staticmethod\n def fetch_ibm_models(base_url: str) -> list[str]:\n \"\"\"Fetch available models from the watsonx.ai API.\"\"\"\n try:\n endpoint = f\"{base_url}/ml/v1/foundation_model_specs\"\n params = {\n \"version\": \"2024-09-16\",\n \"filters\": \"function_embedding,!lifecycle_withdrawn:and\",\n }\n response = requests.get(endpoint, params=params, timeout=10)\n response.raise_for_status()\n data = response.json()\n models = [model[\"model_id\"] for model in data.get(\"resources\", [])]\n return sorted(models)\n except Exception: # noqa: BLE001\n logger.exception(\"Error fetching models\")\n return WATSONX_EMBEDDING_MODEL_NAMES\n async def fetch_ollama_models(self) -> list[str]:\n try:\n return await get_ollama_models(\n base_url_value=self.ollama_base_url,\n desired_capability=DESIRED_CAPABILITY,\n json_models_key=JSON_MODELS_KEY,\n json_name_key=JSON_NAME_KEY,\n json_capabilities_key=JSON_CAPABILITIES_KEY,\n )\n except Exception: # noqa: BLE001\n\n logger.exception(\"Error fetching models\")\n return []\n async def build_embeddings(self) -> Embeddings:\n provider = self.provider\n model = self.model\n api_key = self.api_key\n api_base = self.api_base\n base_url_ibm_watsonx = self.base_url_ibm_watsonx\n ollama_base_url = self.ollama_base_url\n dimensions = self.dimensions\n chunk_size = self.chunk_size\n request_timeout = self.request_timeout\n max_retries = self.max_retries\n show_progress_bar = self.show_progress_bar\n model_kwargs = self.model_kwargs or {}\n\n if provider == \"OpenAI\":\n if not api_key:\n msg = \"OpenAI API key is required when using OpenAI provider\"\n if self.fail_safe_mode:\n logger.error(msg)\n return None\n raise ValueError(msg)\n\n try:\n # Create the primary embedding instance\n embeddings_instance = OpenAIEmbeddings(\n model=model,\n dimensions=dimensions or None,\n base_url=api_base or None,\n api_key=api_key,\n chunk_size=chunk_size,\n max_retries=max_retries,\n timeout=request_timeout or None,\n show_progress_bar=show_progress_bar,\n model_kwargs=model_kwargs,\n )\n\n # Create dedicated instances for each available model\n available_models_dict = {}\n for model_name in OPENAI_EMBEDDING_MODEL_NAMES:\n available_models_dict[model_name] = OpenAIEmbeddings(\n model=model_name,\n dimensions=dimensions or None, # Use same dimensions config for all\n base_url=api_base or None,\n api_key=api_key,\n chunk_size=chunk_size,\n max_retries=max_retries,\n timeout=request_timeout or None,\n show_progress_bar=show_progress_bar,\n model_kwargs=model_kwargs,\n )\n\n return EmbeddingsWithModels(\n embeddings=embeddings_instance,\n available_models=available_models_dict,\n )\n except Exception as e:\n msg = f\"Failed to initialize OpenAI embeddings: {e}\"\n if self.fail_safe_mode:\n logger.error(msg)\n return None\n raise\n\n if provider == \"Ollama\":\n try:\n from langchain_ollama import OllamaEmbeddings\n except ImportError:\n try:\n from langchain_community.embeddings import OllamaEmbeddings\n except ImportError:\n msg = \"Please install langchain-ollama: pip install langchain-ollama\"\n if self.fail_safe_mode:\n logger.error(msg)\n return None\n raise ImportError(msg) from None\n\n try:\n transformed_base_url = transform_localhost_url(ollama_base_url)\n\n # Check if URL contains /v1 suffix (OpenAI-compatible mode)\n if transformed_base_url and transformed_base_url.rstrip(\"/\").endswith(\"/v1\"):\n # Strip /v1 suffix and log warning\n transformed_base_url = transformed_base_url.rstrip(\"/\").removesuffix(\"/v1\")\n logger.warning(\n \"Detected '/v1' suffix in base URL. The Ollama component uses the native Ollama API, \"\n \"not the OpenAI-compatible API. The '/v1' suffix has been automatically removed. \"\n \"If you want to use the OpenAI-compatible API, please use the OpenAI component instead. \"\n \"Learn more at https://docs.ollama.com/openai#openai-compatibility\"\n )\n\n final_base_url = transformed_base_url or \"http://localhost:11434\"\n\n # Create the primary embedding instance\n embeddings_instance = OllamaEmbeddings(\n model=model,\n base_url=final_base_url,\n **model_kwargs,\n )\n\n # Fetch available Ollama models\n available_model_names = await self.fetch_ollama_models()\n\n # Create dedicated instances for each available model\n available_models_dict = {}\n for model_name in available_model_names:\n available_models_dict[model_name] = OllamaEmbeddings(\n model=model_name,\n base_url=final_base_url,\n **model_kwargs,\n )\n\n return EmbeddingsWithModels(\n embeddings=embeddings_instance,\n available_models=available_models_dict,\n )\n except Exception as e:\n msg = f\"Failed to initialize Ollama embeddings: {e}\"\n if self.fail_safe_mode:\n logger.error(msg)\n return None\n raise\n\n if provider == \"IBM watsonx.ai\":\n try:\n from langchain_ibm import WatsonxEmbeddings\n except ImportError:\n msg = \"Please install langchain-ibm: pip install langchain-ibm\"\n if self.fail_safe_mode:\n logger.error(msg)\n return None\n raise ImportError(msg) from None\n\n if not api_key:\n msg = \"IBM watsonx.ai API key is required when using IBM watsonx.ai provider\"\n if self.fail_safe_mode:\n logger.error(msg)\n return None\n raise ValueError(msg)\n\n project_id = self.project_id\n\n if not project_id:\n msg = \"Project ID is required for IBM watsonx.ai provider\"\n if self.fail_safe_mode:\n logger.error(msg)\n return None\n raise ValueError(msg)\n\n try:\n from ibm_watsonx_ai import APIClient, Credentials\n\n final_url = base_url_ibm_watsonx or \"https://us-south.ml.cloud.ibm.com\"\n\n credentials = Credentials(\n api_key=self.api_key,\n url=final_url,\n )\n\n api_client = APIClient(credentials)\n\n params = {\n EmbedTextParamsMetaNames.TRUNCATE_INPUT_TOKENS: self.truncate_input_tokens,\n EmbedTextParamsMetaNames.RETURN_OPTIONS: {\"input_text\": self.input_text},\n }\n\n # Create the primary embedding instance\n embeddings_instance = WatsonxEmbeddings(\n model_id=model,\n params=params,\n watsonx_client=api_client,\n project_id=project_id,\n )\n\n # Fetch available IBM watsonx.ai models\n available_model_names = self.fetch_ibm_models(final_url)\n\n # Create dedicated instances for each available model\n available_models_dict = {}\n for model_name in available_model_names:\n available_models_dict[model_name] = WatsonxEmbeddings(\n model_id=model_name,\n params=params,\n watsonx_client=api_client,\n project_id=project_id,\n )\n\n return EmbeddingsWithModels(\n embeddings=embeddings_instance,\n available_models=available_models_dict,\n )\n except Exception as e:\n msg = f\"Failed to authenticate with IBM watsonx.ai: {e}\"\n if self.fail_safe_mode:\n logger.error(msg)\n return None\n raise\n\n msg = f\"Unknown provider: {provider}\"\n if self.fail_safe_mode:\n logger.error(msg)\n return None\n raise ValueError(msg)\n\n async def update_build_config(\n self, build_config: dotdict, field_value: Any, field_name: str | None = None\n ) -> dotdict:\n # Handle fail_safe_mode changes first - set all required fields to False if enabled\n if field_name == \"fail_safe_mode\":\n if field_value: # If fail_safe_mode is enabled\n build_config[\"api_key\"][\"required\"] = False\n elif hasattr(self, \"provider\"):\n # If fail_safe_mode is disabled, restore required flags based on provider\n if self.provider in [\"OpenAI\", \"IBM watsonx.ai\"]:\n build_config[\"api_key\"][\"required\"] = True\n else: # Ollama\n build_config[\"api_key\"][\"required\"] = False\n\n if field_name == \"provider\":\n if field_value == \"OpenAI\":\n build_config[\"model\"][\"options\"] = OPENAI_EMBEDDING_MODEL_NAMES\n build_config[\"model\"][\"value\"] = OPENAI_EMBEDDING_MODEL_NAMES[0]\n build_config[\"api_key\"][\"display_name\"] = \"OpenAI API Key\"\n # Only set required=True if fail_safe_mode is not enabled\n build_config[\"api_key\"][\"required\"] = not (hasattr(self, \"fail_safe_mode\") and self.fail_safe_mode)\n build_config[\"api_key\"][\"show\"] = True\n build_config[\"api_base\"][\"display_name\"] = \"OpenAI API Base URL\"\n build_config[\"api_base\"][\"advanced\"] = True\n build_config[\"api_base\"][\"show\"] = True\n build_config[\"ollama_base_url\"][\"show\"] = False\n build_config[\"project_id\"][\"show\"] = False\n build_config[\"base_url_ibm_watsonx\"][\"show\"] = False\n build_config[\"truncate_input_tokens\"][\"show\"] = False\n build_config[\"input_text\"][\"show\"] = False\n elif field_value == \"Ollama\":\n build_config[\"ollama_base_url\"][\"show\"] = True\n\n if await is_valid_ollama_url(url=self.ollama_base_url):\n try:\n models = await self.fetch_ollama_models()\n build_config[\"model\"][\"options\"] = models\n build_config[\"model\"][\"value\"] = models[0] if models else \"\"\n except ValueError:\n build_config[\"model\"][\"options\"] = []\n build_config[\"model\"][\"value\"] = \"\"\n else:\n build_config[\"model\"][\"options\"] = []\n build_config[\"model\"][\"value\"] = \"\"\n build_config[\"truncate_input_tokens\"][\"show\"] = False\n build_config[\"input_text\"][\"show\"] = False\n build_config[\"api_key\"][\"display_name\"] = \"API Key (Optional)\"\n build_config[\"api_key\"][\"required\"] = False\n build_config[\"api_key\"][\"show\"] = False\n build_config[\"api_base\"][\"show\"] = False\n build_config[\"project_id\"][\"show\"] = False\n build_config[\"base_url_ibm_watsonx\"][\"show\"] = False\n\n elif field_value == \"IBM watsonx.ai\":\n build_config[\"model\"][\"options\"] = self.fetch_ibm_models(base_url=self.base_url_ibm_watsonx)\n build_config[\"model\"][\"value\"] = self.fetch_ibm_models(base_url=self.base_url_ibm_watsonx)[0]\n build_config[\"api_key\"][\"display_name\"] = \"IBM watsonx.ai API Key\"\n # Only set required=True if fail_safe_mode is not enabled\n build_config[\"api_key\"][\"required\"] = not (hasattr(self, \"fail_safe_mode\") and self.fail_safe_mode)\n build_config[\"api_key\"][\"show\"] = True\n build_config[\"api_base\"][\"show\"] = False\n build_config[\"ollama_base_url\"][\"show\"] = False\n build_config[\"base_url_ibm_watsonx\"][\"show\"] = True\n build_config[\"project_id\"][\"show\"] = True\n build_config[\"truncate_input_tokens\"][\"show\"] = True\n build_config[\"input_text\"][\"show\"] = True\n elif field_name == \"base_url_ibm_watsonx\":\n build_config[\"model\"][\"options\"] = self.fetch_ibm_models(base_url=field_value)\n build_config[\"model\"][\"value\"] = self.fetch_ibm_models(base_url=field_value)[0]\n elif field_name == \"ollama_base_url\":\n # # Refresh Ollama models when base URL changes\n # if hasattr(self, \"provider\") and self.provider == \"Ollama\":\n # Use field_value if provided, otherwise fall back to instance attribute\n ollama_url = self.ollama_base_url\n if await is_valid_ollama_url(url=ollama_url):\n try:\n models = await self.fetch_ollama_models()\n build_config[\"model\"][\"options\"] = models\n build_config[\"model\"][\"value\"] = models[0] if models else \"\"\n except ValueError:\n await logger.awarning(\"Failed to fetch Ollama embedding models.\")\n build_config[\"model\"][\"options\"] = []\n build_config[\"model\"][\"value\"] = \"\"\n\n elif field_name == \"model\" and self.provider == \"Ollama\":\n ollama_url = self.ollama_base_url\n if await is_valid_ollama_url(url=ollama_url):\n try:\n models = await self.fetch_ollama_models()\n build_config[\"model\"][\"options\"] = models\n except ValueError:\n await logger.awarning(\"Failed to refresh Ollama embedding models.\")\n build_config[\"model\"][\"options\"] = []\n\n return build_config\n"
},
"dimensions": {
"_input_type": "IntInput",
"advanced": true,
"display_name": "Dimensions",
"dynamic": false,
"info": "The number of dimensions the resulting output embeddings should have. Only supported by certain models.",
"list": false,
"list_add_label": "Add More",
"name": "dimensions",
"placeholder": "",
</shell_result>
langchain_openai 0.3.23 OpenAIEmbeddings API documentation parameters
<web_search_result>
Here are the init parameters (and key attributes) for langchain_openai.OpenAIEmbeddings (reference for v0.3.23):
- model — model name to use (e.g., "text-embedding-3-large"). [1]
- dimensions — desired embedding dimensionality (supported for text-embedding-3 models). [1]
- openai_api_key / api_key — OpenAI API key (can be a str or callable/async callable). [1]
- openai_organization / organization — OpenAI org ID (falls back to env var). [1]
- openai_api_base / base_url — custom base URL for API (useful for proxies / emulators). [1]
- openai_api_version / api_version — OpenAI API version (from env if not provided). [1]
- embedding_ctx_length — max tokens to embed at once (default 8191). [1]
- chunk_size — max number of texts per batch (default 1000). [1]
- max_retries — max retry attempts (default 2). [1]
- request_timeout / timeout — request timeout (float, tuple, httpx.Timeout, or None). [1]
- tiktoken_enabled — whether to use tiktoken for token counting (default True). [1]
- tiktoken_model_name — model name for tiktoken (optional override). [1]
- show_progress_bar — show progress bar while embedding (default False). [1]
- model_kwargs — dict of extra model kwargs passed through to the client. [1]
- skip_empty — skip empty documents when embedding. [1]
- retry_min_seconds / retry_max_seconds — backoff window for retries. [1]
- http_client / http_async_client — custom HTTP client instances. [1]
- check_embedding_ctx_length, build_extra, validate_environment — utility methods available on the class. [1]
Usage/instantiation examples and higher-level docs are at the LangChain OpenAI embeddings guide. [2]
Sources: LangChain reference for OpenAIEmbeddings (params list). [1][2]
References
[1] LangChain Reference — OpenAIEmbeddings (params & attributes).
[2] LangChain docs — OpenAIEmbeddings overview / instantiation.
</web_search_result>
langchain_ibm 0.3.19 WatsonxEmbeddings API documentation parameters
<web_search_result>
Summary of WatsonxEmbeddings (langchain_ibm v0.3.19) — main constructor parameters and API surface
Constructor / init parameters (most important):
- model, model_id — choose exactly one (mutually exclusive). [1][2]
- params — dict of model-specific embedding parameters (e.g., EmbedTextParamsMetaNames values). [1][6]
- watsonx_client / api_client — optional IBM APIClient instance to reuse. [1][4]
- api_key / WATSONX_API_KEY (environment or api_key arg) — authentication. (note: older apikey / WATSONX_APIKEY deprecated). [1][3]
- url (WATSONX_URL) — Watson Machine Learning / service URL. [1][2]
- project_id or space_id — required context: at least one must be provided for calls. [1][6]
- token, username, password, instance_id, version — other CPD/cluster credential options. [2][4]
- verify — TLS verification setting (bool or path to CA bundle). [2][4]
Methods / call signatures:
- embed_documents(texts: list[str], **kwargs) -> list[list[float]] — embed multiple texts; kwargs passed to underlying client. [2][3]
- embed_query(text: str, **kwargs) -> list[float] — embed single query; kwargs passed through. [2][3]
- async variants: aembed_documents, aembed_query. [2][3]
Behavior notes:
- params should use IBM SDK meta names (e.g., EmbedTextParamsMetaNames.TRUNCATE_INPUT_TOKENS, RETURN_OPTIONS). [1][6]
- If using Model Gateway vs direct SDK, the integration supports both (via watsonx_embed_gateway vs watsonx_embed). [4]
Sources:
- LangChain reference: WatsonxEmbeddings docs. [1]
- LangChain API reference (python) / class docstring and methods. [2]
- LangChain source module (langchain_ibm.embeddings) showing internals and parameter checks. [3]
</web_search_result>
Based on my verification, I can now provide the rewritten review comment. The issues identified in the original review are all valid:
- IBM model fetching is called twice in both the
"IBM watsonx.ai"provider branch and the"base_url_ibm_watsonx"field handler - Ollama base URL refresh ignores the incoming parameter, using
self.ollama_base_urlinstead offield_value - Return type annotation mismatch:
build_embeddingsdeclares return type asEmbeddingsbut can returnNonewhenfail_safe_modeis enabled - Missing guards on list indexing: accessing
[0]on potentially empty model lists could cause IndexError - API signatures verified: Both
langchain_openai.OpenAIEmbeddings(v0.3.23) andlangchain_ibm.WatsonxEmbeddings(v0.3.19) accept the parameters used in the code
EmbeddingModelComponent: fix double IBM model fetches, use field_value for Ollama URL refresh, and correct return type annotation.
Three concrete issues in update_build_config:
- IBM model fetching called twice (lines ~447–448 and ~455–456): Store result in a variable to avoid redundant API calls.
- Ollama base URL handler ignores
field_valueparameter (line ~459): Should usefield_value or self.ollama_base_urlto respect the incoming field value. - Index access on potentially empty lists: Accessing
[0]without checking list length; guard with conditionals (e.g.,models[0] if models else ""). - Return type annotation mismatch in
build_embeddings(line ~348): Currently returnsEmbeddingsbut can returnNonewhenfail_safe_modeis enabled. Update signature toasync def build_embeddings(self) -> Embeddings | None:.
Apply this minimal diff to the embedded Python code:
elif field_value == "IBM watsonx.ai":
- build_config["model"]["options"] = self.fetch_ibm_models(base_url=self.base_url_ibm_watsonx)
- build_config["model"]["value"] = self.fetch_ibm_models(base_url=self.base_url_ibm_watsonx)[0]
+ _models = self.fetch_ibm_models(base_url=self.base_url_ibm_watsonx)
+ build_config["model"]["options"] = _models
+ build_config["model"]["value"] = _models[0] if _models else ""
elif field_name == "base_url_ibm_watsonx":
- build_config["model"]["options"] = self.fetch_ibm_models(base_url=field_value)
- build_config["model"]["value"] = self.fetch_ibm_models(base_url=field_value)[0]
+ _models = self.fetch_ibm_models(base_url=field_value)
+ build_config["model"]["options"] = _models
+ build_config["model"]["value"] = _models[0] if _models else ""
elif field_name == "ollama_base_url":
- ollama_url = self.ollama_base_url
+ ollama_url = field_value or self.ollama_base_url
if await is_valid_ollama_url(url=ollama_url):Also update the method signature:
- async def build_embeddings(self) -> Embeddings:
+ async def build_embeddings(self) -> Embeddings | None:🤖 Prompt for AI Agents
In src/backend/base/langflow/initial_setup/starter_projects/Nvidia Remix.json
around build_embeddings (~line 348) and update_build_config (~lines 447–459),
fix three issues: (1) change build_embeddings signature to return Embeddings |
None (e.g., async def build_embeddings(self) -> Embeddings | None) because the
method can return None when fail_safe_mode is enabled; (2) in
update_build_config when handling the "IBM watsonx.ai" provider and
"base_url_ibm_watsonx" field, avoid calling fetch_ibm_models twice by calling it
once, storing the result in a local variable, and using that variable for both
options and value, guarding index access (use models[0] if models else "" or
similar); (3) in the "ollama_base_url" handler use the incoming field_value
(e.g., ollama_url = field_value or self.ollama_base_url) when validating and
refreshing models, and guard any list indexing there as well (models[0] if
models else "") to prevent IndexError.
| logger.debug( | ||
| f"[INGESTION] ingest_data type: {type(self.ingest_data)}, length: {len(self.ingest_data) if self.ingest_data else 0}" | ||
| ) | ||
| logger.debug( | ||
| f"[INGESTION] ingest_data content: {self.ingest_data[:2] if self.ingest_data and len(self.ingest_data) > 0 else 'empty'}" | ||
| ) |
There was a problem hiding this comment.
Fix line length violations to pass linting.
Lines 674 and 677 exceed the 120-character limit, causing the Ruff style check to fail.
Apply this diff to fix the line length:
- logger.debug(
- f"[INGESTION] ingest_data type: {type(self.ingest_data)}, length: {len(self.ingest_data) if self.ingest_data else 0}"
- )
- logger.debug(
- f"[INGESTION] ingest_data content: {self.ingest_data[:2] if self.ingest_data and len(self.ingest_data) > 0 else 'empty'}"
- )
+ ingest_len = len(self.ingest_data) if self.ingest_data else 0
+ logger.debug(f"[INGESTION] ingest_data type: {type(self.ingest_data)}, length: {ingest_len}")
+ ingest_preview = self.ingest_data[:2] if self.ingest_data and len(self.ingest_data) > 0 else "empty"
+ logger.debug(f"[INGESTION] ingest_data content: {ingest_preview}")📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| logger.debug( | |
| f"[INGESTION] ingest_data type: {type(self.ingest_data)}, length: {len(self.ingest_data) if self.ingest_data else 0}" | |
| ) | |
| logger.debug( | |
| f"[INGESTION] ingest_data content: {self.ingest_data[:2] if self.ingest_data and len(self.ingest_data) > 0 else 'empty'}" | |
| ) | |
| ingest_len = len(self.ingest_data) if self.ingest_data else 0 | |
| logger.debug(f"[INGESTION] ingest_data type: {type(self.ingest_data)}, length: {ingest_len}") | |
| ingest_preview = self.ingest_data[:2] if self.ingest_data and len(self.ingest_data) > 0 else "empty" | |
| logger.debug(f"[INGESTION] ingest_data content: {ingest_preview}") |
🧰 Tools
🪛 GitHub Actions: Ruff Style Check
[error] 674-674: Ruff: E501 Line too long (129 > 120).
🪛 GitHub Check: Ruff Style Check (3.13)
[failure] 677-677: Ruff (E501)
src/lfx/src/lfx/components/elastic/opensearch_multimodal.py:677:121: E501 Line too long (133 > 120)
[failure] 674-674: Ruff (E501)
src/lfx/src/lfx/components/elastic/opensearch_multimodal.py:674:121: E501 Line too long (129 > 120)
🤖 Prompt for AI Agents
In src/lfx/src/lfx/components/elastic/opensearch_multimodal.py around lines 673
to 678, the two logger.debug lines exceed the 120-character limit; split or
reformat their f-strings so each line stays under 120 chars (for example, assign
the conditional parts to short local variables or break the f-string into
multiple concatenated parts) and keep the original log content and semantics
intact.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #10726 +/- ##
=======================================
Coverage 32.43% 32.43%
=======================================
Files 1367 1367
Lines 63315 63315
Branches 9357 9357
=======================================
+ Hits 20537 20538 +1
Misses 41745 41745
+ Partials 1033 1032 -1
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/backend/base/langflow/initial_setup/starter_projects/Nvidia Remix.json (1)
2018-2018: Align fail‑safe behavior with type hints and refresh logic (signature + config fixes).Build can return None under fail_safe_mode; update type. Also remove duplicate IBM fetches, guard empty lists, and honor field_value for Ollama URL refresh.
Apply minimal diffs inside the embedded code string:
- async def build_embeddings(self) -> Embeddings: + async def build_embeddings(self) -> Embeddings | None: @@ - elif field_value == "IBM watsonx.ai": - build_config["model"]["options"] = self.fetch_ibm_models(base_url=self.base_url_ibm_watsonx) - build_config["model"]["value"] = self.fetch_ibm_models(base_url=self.base_url_ibm_watsonx)[0] + elif field_value == "IBM watsonx.ai": + _models = self.fetch_ibm_models(base_url=self.base_url_ibm_watsonx) + build_config["model"]["options"] = _models + build_config["model"]["value"] = _models[0] if _models else "" @@ - elif field_name == "base_url_ibm_watsonx": - build_config["model"]["options"] = self.fetch_ibm_models(base_url=field_value) - build_config["model"]["value"] = self.fetch_ibm_models(base_url=field_value)[0] + elif field_name == "base_url_ibm_watsonx": + _models = self.fetch_ibm_models(base_url=field_value) + build_config["model"]["options"] = _models + build_config["model"]["value"] = _models[0] if _models else "" @@ - elif field_name == "ollama_base_url": + elif field_name == "ollama_base_url": @@ - ollama_url = self.ollama_base_url + ollama_url = field_value or self.ollama_base_url if await is_valid_ollama_url(url=ollama_url): try: models = await self.fetch_ollama_models() build_config["model"]["options"] = models - build_config["model"]["value"] = models[0] if models else "" + build_config["model"]["value"] = models[0] if models else "" except ValueError: await logger.awarning("Failed to fetch Ollama embedding models.") build_config["model"]["options"] = [] build_config["model"]["value"] = ""
🧹 Nitpick comments (2)
src/backend/base/langflow/initial_setup/starter_projects/Nvidia Remix.json (2)
2038-2058: Fail‑safe UX: good addition; ensure downstream can handle None embeddings.Since build_embeddings may return None, confirm FAISS/consumers tolerate None and surface a clear UI error instead of crashing. If not, add a guard where EmbeddingModel is consumed.
2018-2018: Minor: use field_value during provider switch for Ollama as well.In the "provider == 'Ollama'" branch you validate self.ollama_base_url; prefer field_value when switching providers from an already‑edited base URL to avoid a stale check. Optional, but prevents a 1‑click lag.
- if await is_valid_ollama_url(url=self.ollama_base_url): + if await is_valid_ollama_url(url=getattr(self, "ollama_base_url", None)):
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/backend/base/langflow/initial_setup/starter_projects/Nvidia Remix.json(3 hunks)
🔇 Additional comments (1)
src/backend/base/langflow/initial_setup/starter_projects/Nvidia Remix.json (1)
1856-1856: I'll verify the code_hash inconsistency by examining the JSON file and computing hashes of the embedded code.
<function_calls>
#!/bin/bashFirst, check file size and line count
wc -l "src/backend/base/langflow/initial_setup/starter_projects/Nvidia Remix.json"
</function_calls>
<function_calls>
#!/bin/bashGet context around line 1856 to find the code_hash and its associated code
sed -n '1850,1870p' "src/backend/base/langflow/initial_setup/starter_projects/Nvidia Remix.json"
</function_calls>
<function_calls>
#!/bin/bashSearch for all code_hash occurrences and their context
rg -n 'code_hash' "src/backend/base/langflow/initial_setup/starter_projects/Nvidia Remix.json" -A 2 -B 2
</function_calls>
This pull request makes a minor update to the
src/backend/base/langflow/initial_setup/starter_projects/Nvidia Remix.jsonfile, changing thecode_hashvalue in the project's metadata. This is likely to reflect an update in the underlying code or dependencies.code_hashin themetadatasection to0e2d6fe67a26to reflect the latest code version.Summary by CodeRabbit
New Features
Bug Fixes
✏️ Tip: You can customize this high-level summary in your review settings.