-
Notifications
You must be signed in to change notification settings - Fork 8.2k
fix: add fail safe mode for embeddings model #10726
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.