Skip to content

Conversation

@edwinjosechittilappilly
Copy link
Collaborator

@edwinjosechittilappilly edwinjosechittilappilly commented Jan 26, 2026

Enhances provider variable handling by introducing a metadata structure for all required variables per provider, including support for providers with multiple required variables (e.g., IBM WatsonX). Updates backend API to return full variable info, adapts frontend to dynamically render and save multiple variables, and adds comprehensive tests for the new API response. Also updates static mappings and introduces a new React hook for fetching provider variable metadata.

Summary by CodeRabbit

  • New Features

    • Provider configuration now supports multiple variables per provider instead of just API keys, enabling complex multi-field setups like IBM WatsonX (API key, Project ID, URL)
    • Enhanced provider metadata endpoint returns detailed variable information including descriptions, required flags, and secret indicators
    • Updated configuration interface dynamically renders appropriate input types per variable
  • Tests

    • Added comprehensive test coverage for provider variable mapping and backward compatibility
  • Documentation

    • Added deprecation notice directing to the new provider metadata API endpoint

✏️ Tip: You can customize this high-level summary in your review settings.

Enhances provider variable handling by introducing a metadata structure for all required variables per provider, including support for providers with multiple required variables (e.g., IBM WatsonX). Updates backend API to return full variable info, adapts frontend to dynamically render and save multiple variables, and adds comprehensive tests for the new API response. Also updates static mappings and introduces a new React hook for fetching provider variable metadata.
Copilot AI review requested due to automatic review settings January 26, 2026 16:50
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 26, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • 🔍 Trigger a full review

Walkthrough

The changes extend provider variable configuration from a single API key per provider to support multiple variables with metadata. The backend API endpoint returns enriched variable information including required, secret, and option fields. Frontend, service, and core model logic are updated to handle multi-variable validation, storage, and UI rendering, with new utility functions for variable lookup and IBM WatsonX multi-variable support.

Changes

Cohort / File(s) Summary
Backend API Endpoint
src/backend/base/langflow/api/v1/models.py
Updated /provider-variable-mapping endpoint to return full variable metadata objects per provider instead of simple string mappings; changed return type from dict[str, str] to dict[str, list[dict]] and added import for get_model_provider_metadata.
Backend Service Logic
src/backend/base/langflow/services/variable/service.py
Enhanced variable service to build comprehensive provider-to-variable mappings using full metadata; introduced secret vs non-secret variable handling; replaced per-key API key logic with metadata-driven validation and default field configuration.
Backend Tests
src/backend/tests/unit/api/v1/test_models_enabled_providers.py
Added three test cases validating provider-variable-mapping endpoint behavior, multi-variable providers (IBM WatsonX), and backward compatibility of primary variable mapping.
Frontend Type Definitions
src/frontend/src/constants/providerConstants.ts
Added new ProviderVariable interface with fields for variable metadata (name, key, description, required, is_secret, is_list, options); marked existing PROVIDER_VARIABLE_MAPPING as deprecated.
Frontend API Hook
src/frontend/src/controllers/API/queries/models/use-get-provider-variables.ts
Introduced new useGetProviderVariables hook to fetch provider variables from API with caching and error handling; defined ProviderVariablesMapping type.
Frontend UI Component
src/frontend/src/modals/modelProviderModal/components/ModelProvidersContent.tsx
Refactored to support multi-variable configuration with batch save workflow; added masked value display for secrets; replaced single API key input with dynamic variable inputs per provider; updated validation and success feedback logic.
LFX Package Exports
src/lfx/src/lfx/base/models/__init__.py
Re-exported four new utility functions from unified_models in public API: get_model_provider_metadata, get_provider_all_variables, get_provider_from_variable_key, get_provider_required_variable_keys.
LFX Core Model Logic
src/lfx/src/lfx/base/models/unified_models.py
Restructured provider metadata from single variable_name to variables array with detailed metadata per variable; added utility functions for variable lookup and required variable filtering; updated validation logic to check all required variables per provider; enhanced IBM WatsonX to support API key, project ID, and URL.
LFX Environment Constants
src/lfx/src/lfx/services/settings/constants.py
Added three IBM WatsonX environment variables (WATSONX_APIKEY, WATSONX_PROJECT_ID, WATSONX_URL) to VARIABLES_TO_GET_FROM_ENVIRONMENT list.

Sequence Diagram(s)

sequenceDiagram
    participant FrontendUI as Frontend UI
    participant APIHook as API Hook
    participant BackendAPI as Backend API
    participant Service as Variable Service
    participant LFXCore as LFX Core
    participant Storage as Variable Storage

    FrontendUI->>APIHook: useGetProviderVariables()
    APIHook->>BackendAPI: GET /api/v1/models/provider-variable-mapping
    BackendAPI->>LFXCore: get_model_provider_metadata()
    LFXCore-->>BackendAPI: {provider: [{variable_key, required, is_secret, options, ...}]}
    BackendAPI-->>APIHook: Provider variables mapping
    APIHook-->>FrontendUI: Update state with variable metadata

    FrontendUI->>FrontendUI: Render dynamic inputs for each variable
    Note over FrontendUI: Display required/secret flags,<br/>dropdowns for options, text inputs

    FrontendUI->>FrontendUI: User enters/selects values for all variables
    FrontendUI->>BackendAPI: POST/PATCH save all variables (batch)
    BackendAPI->>Service: Process variable updates
    Service->>LFXCore: get_provider_required_variable_keys(provider)
    LFXCore-->>Service: Required variable keys for provider
    Service->>Storage: Validate & store all required variables
    Storage-->>Service: Success/error per variable
    Service->>LFXCore: _validate_and_get_enabled_providers()
    LFXCore-->>Service: Enabled providers (all required vars present)
    Service-->>BackendAPI: Success/aggregated errors
    BackendAPI-->>FrontendUI: Update status & clear inputs
    FrontendUI->>FrontendUI: Mark provider as enabled, refresh queries
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 2 warnings)
Check name Status Explanation Resolution
Test Coverage For New Implementations ❌ Error PR adds backend tests for the new provider-variable-mapping endpoint but omits frontend test coverage for the new useGetProviderVariables hook and ModelProvidersContent component refactoring despite significant new functionality. Add unit tests for useGetProviderVariables hook and ModelProvidersContent component; fix unused fixtures in test_backward_compatible_variable_mapping; update hook error handling to rethrow instead of returning empty objects.
Test Quality And Coverage ⚠️ Warning Test coverage is significantly incomplete with no frontend tests for 560-line component changes, zero tests for new hook, and limited backend smoke tests only. Add comprehensive tests for backend error scenarios, frontend hook including error cases, component multi-variable rendering, batch save, secret masking, and service layer logic.
Test File Naming And Structure ⚠️ Warning Test file has unused fixtures (ARG001 violation), no frontend test file for use-get-provider-variables.ts, and backend tests lack error/edge case coverage. Remove unused fixtures from test_backward_compatible_variable_mapping, create frontend test file with error scenarios, add backend error condition tests.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly matches the main objective of adding full provider variable metadata and multi-variable support across the codebase.
Docstring Coverage ✅ Passed Docstring coverage is 86.67% which is sufficient. The required threshold is 80.00%.
Excessive Mock Usage Warning ✅ Passed The test file demonstrates appropriate and minimal mock usage targeting only external dependencies, with new provider-variable-mapping tests using zero mocks for real API behavior validation.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch EJ/add-multi-api-support

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added the enhancement New feature or request label Jan 26, 2026
@github-actions github-actions bot added enhancement New feature or request and removed enhancement New feature or request labels Jan 26, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Jan 26, 2026

Frontend Unit Test Coverage Report

Coverage Summary

Lines Statements Branches Functions
Coverage: 17%
17.49% (5050/28865) 10.92% (2432/22259) 11.59% (733/6320)

Unit Test Results

Tests Skipped Failures Errors Time
2036 0 💤 0 ❌ 0 🔥 25.832s ⏱️

@codecov
Copy link

codecov bot commented Jan 26, 2026

Codecov Report

❌ Patch coverage is 18.27676% with 313 lines in your changes missing coverage. Please review.
✅ Project coverage is 32.26%. Comparing base (1529f56) to head (1fc5afd).

Files with missing lines Patch % Lines
src/lfx/src/lfx/base/models/unified_models.py 13.04% 160 Missing ⚠️
...ProviderModal/components/ModelProvidersContent.tsx 0.00% 116 Missing ⚠️
...s/API/queries/models/use-get-provider-variables.ts 0.00% 14 Missing ⚠️
src/backend/base/langflow/api/v1/models.py 35.29% 11 Missing ⚠️
...backend/base/langflow/services/variable/service.py 78.57% 6 Missing ⚠️
src/lfx/src/lfx/base/models/model_metadata.py 73.91% 2 Missing and 4 partials ⚠️

❌ Your patch check has failed because the patch coverage (18.27%) is below the target coverage (40.00%). You can increase the patch coverage or adjust the target coverage.
❌ Your project check has failed because the head coverage (44.75%) is below the target coverage (55.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main   #11446      +/-   ##
==========================================
- Coverage   34.84%   32.26%   -2.58%     
==========================================
  Files        1420     1421       +1     
  Lines       68190    68465     +275     
  Branches     9979    10065      +86     
==========================================
- Hits        23762    22093    -1669     
- Misses      43203    45134    +1931     
- Partials     1225     1238      +13     
Flag Coverage Δ
backend 44.75% <62.22%> (-9.46%) ⬇️
frontend 16.01% <0.76%> (-0.05%) ⬇️
lfx 41.54% <19.80%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/frontend/src/components/ui/input.tsx 56.52% <ø> (ø)
src/frontend/src/constants/providerConstants.ts 100.00% <100.00%> (ø)
src/lfx/src/lfx/services/settings/constants.py 100.00% <ø> (ø)
...backend/base/langflow/services/variable/service.py 51.70% <78.57%> (-34.61%) ⬇️
src/lfx/src/lfx/base/models/model_metadata.py 83.78% <73.91%> (-16.22%) ⬇️
src/backend/base/langflow/api/v1/models.py 25.68% <35.29%> (-4.26%) ⬇️
...s/API/queries/models/use-get-provider-variables.ts 0.00% <0.00%> (ø)
...ProviderModal/components/ModelProvidersContent.tsx 0.00% <0.00%> (ø)
src/lfx/src/lfx/base/models/unified_models.py 26.40% <13.04%> (+2.69%) ⬆️

... and 102 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions github-actions bot added enhancement New feature or request and removed enhancement New feature or request labels Jan 26, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds structured provider variable metadata (supporting multiple required variables per provider) and updates backend + frontend to use the richer mapping, including tests for the new API response.

Changes:

  • Backend: replaces single variable_name mapping with per-provider variables[] metadata and exposes it via /provider-variable-mapping.
  • Frontend: introduces a new query hook and updates the provider modal to render/save multiple variables dynamically.
  • Tests: adds unit coverage to validate the new endpoint response and backward-compatible mapping behavior.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
src/lfx/src/lfx/services/settings/constants.py Adds IBM WatsonX env vars to the global “variables to load” list.
src/lfx/src/lfx/base/models/unified_models.py Introduces multi-variable provider metadata and updates provider enablement/validation logic.
src/lfx/src/lfx/base/models/init.py Re-exports new provider-variable helper functions.
src/backend/base/langflow/api/v1/models.py Updates provider-variable-mapping endpoint to return full variable metadata.
src/backend/base/langflow/services/variable/service.py Uses provider metadata to set default_fields for all provider variables and validates secrets.
src/frontend/src/constants/providerConstants.ts Adds ProviderVariable interface and deprecates static mapping usage.
src/frontend/src/controllers/API/queries/models/use-get-provider-variables.ts Adds hook to fetch provider variable metadata from the API.
src/frontend/src/modals/modelProviderModal/components/ModelProvidersContent.tsx Updates UI to render/save multiple variables, including dropdown options.
src/backend/tests/unit/api/v1/test_models_enabled_providers.py Adds tests for the new endpoint response and IBM WatsonX multi-variable behavior.
Files not reviewed (1)
  • src/frontend/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 394 to 396
for provider in provider_variable_map:
# Get all required variable keys for this provider
required_var_keys = get_provider_required_variable_keys(provider)
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_validate_and_get_enabled_providersdecrypts every required variable value. With the new metadata, some required variables are explicitly non-secret (e.g.,WATSONX_PROJECT_ID, WATSONX_URL) and will likely be stored unencrypted; attempting to decrypt those will fail and prevent the provider from ever being enabled. Fix by looking up each required variable’s metadata and only decrypting values for is_secret=True; for non-secret variables, validate presence using the raw stored value instead of decrypt_api_key` (and ensure the function is passed the right variable set if it currently only includes credentials).

Copilot uses AI. Check for mistakes.
Comment on lines 441 to 442

return enabled
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will mark any provider with zero “required” variables as enabled unconditionally. For Ollama (where the base URL is optional in metadata), this is a behavior change vs. the previous logic (which only enabled providers when a corresponding variable existed). Consider requiring that some provider variable exists (e.g., the “primary” variable from provider_variable_map[provider] or any variable in storage) before adding it to enabled, rather than enabling by default.

Suggested change
return enabled
# Provider has no required variables (like Ollama with optional base URL).
# Only enable the provider if at least one configured variable exists
# (for example, the "primary" variable from provider_variable_map).
primary_var_name = provider_variable_map.get(provider)
if primary_var_name and primary_var_name in credential_variables:
enabled.add(provider)

Copilot uses AI. Check for mistakes.
Comment on lines 120 to 127
if (selectedProvider && isUserInputRef.current) {
// Find the variable that was just changed and has a value
const variableToSave = providerVariables.find((v) =>
variableValues[v.variable_key]?.trim(),
);
if (variableToSave) {
handleSaveVariable(variableToSave);
}
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The debounced autosave picks the first variable in providerVariables that currently has a non-empty value, not the variable that was last edited. For providers with multiple fields (e.g., WatsonX), once the first field is filled, subsequent edits to other fields will keep re-saving the first field and may never persist the others. Track the “last changed variable_key” (e.g., via a ref/state set in each onChange/onValueChange handler) and save that specific variable instead of using find(...).

Copilot uses AI. Check for mistakes.

const onError = (error: any) => {
setValidationFailed(true);
setSavingVariableKey(null);
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On error you clear savingVariableKey, but the UI’s error indicator is gated on savingVariableKey === variable.variable_key. This makes it impossible to render the error icon for the field that failed. Use a separate piece of state for “failedVariableKey” (or keep savingVariableKey until the UI can display and the user edits again) so the error is attributable to the correct input.

Suggested change
setSavingVariableKey(null);

Copilot uses AI. Check for mistakes.
Comment on lines 456 to 465
endIcon={
isSaving
? "LoaderCircle"
: validationFailed &&
savingVariableKey === variable.variable_key
? "X"
: isConfigured
? "Check"
: undefined
}
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because savingVariableKey is set to null in onError, the condition validationFailed && savingVariableKey === variable.variable_key will never be true after a failed save, so the “X” icon won’t show. Tie the error state to the failing variable via a dedicated failedVariableKey, or don’t null out the key until after the error is rendered/acknowledged.

Copilot uses AI. Check for mistakes.
Comment on lines +190 to 205
async def get_model_provider_mapping() -> dict[str, list[dict]]:
"""Return provider variables mapping with full variable info.
Each provider maps to a list of variable objects containing:
- variable_name: Display name shown to user
- variable_key: Environment variable key
- description: Help text for the variable
- required: Whether the variable is required
- is_secret: Whether to treat as credential
- is_list: Whether it accepts multiple values
- options: Predefined options for dropdowns
"""
metadata = get_model_provider_metadata()
return {provider: meta.get("variables", []) for provider, meta in metadata.items()}


Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This endpoint changes its response shape from dict[str, str] (provider → variable key) to dict[str, list[dict]] (provider → variable metadata list). Since the path stayed the same, existing clients expecting the prior schema will break. If backward compatibility is required, consider either (a) introducing a new endpoint (e.g., /provider-variables), (b) adding a query param (e.g., ?format=full|legacy), or (c) returning both shapes (e.g., { mapping: ..., variables: ... }) during a deprecation window.

Suggested change
async def get_model_provider_mapping() -> dict[str, list[dict]]:
"""Return provider variables mapping with full variable info.
Each provider maps to a list of variable objects containing:
- variable_name: Display name shown to user
- variable_key: Environment variable key
- description: Help text for the variable
- required: Whether the variable is required
- is_secret: Whether to treat as credential
- is_list: Whether it accepts multiple values
- options: Predefined options for dropdowns
"""
metadata = get_model_provider_metadata()
return {provider: meta.get("variables", []) for provider, meta in metadata.items()}
async def get_model_provider_mapping(
format: Annotated[
str,
Query(
description="Response format: 'legacy' for simple mapping, 'full' for variable metadata.",
alias="format",
),
] = "legacy",
) -> dict:
"""Return provider variables mapping.
Backwards compatible behavior:
- By default (format=legacy), returns a simple mapping of provider -> variable key,
matching the original endpoint behavior:
{
"openai": "OPENAI_API_KEY",
"anthropic": "ANTHROPIC_API_KEY",
...
}
- When format=full, returns provider -> list of variable metadata objects:
{
"openai": [
{
"variable_name": "...",
"variable_key": "...",
"description": "...",
"required": true,
"is_secret": true,
"is_list": false,
"options": [...]
},
...
],
...
}
"""
if format == "full":
metadata = get_model_provider_metadata()
return {provider: meta.get("variables", []) for provider, meta in metadata.items()}
# Default: legacy simple mapping (provider -> variable key)
return get_model_provider_variable_mapping()

Copilot uses AI. Check for mistakes.
Comment on lines +202 to +203
metadata = get_model_provider_metadata()
return {provider: meta.get("variables", []) for provider, meta in metadata.items()}
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This endpoint changes its response shape from dict[str, str] (provider → variable key) to dict[str, list[dict]] (provider → variable metadata list). Since the path stayed the same, existing clients expecting the prior schema will break. If backward compatibility is required, consider either (a) introducing a new endpoint (e.g., /provider-variables), (b) adding a query param (e.g., ?format=full|legacy), or (c) returning both shapes (e.g., { mapping: ..., variables: ... }) during a deprecation window.

Copilot uses AI. Check for mistakes.
Comment on lines +436 to +438
# Check OpenAI primary variable
openai_api_key_var = openai_vars[0]
assert openai_api_key_var["variable_key"] == "OPENAI_API_KEY"
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test assumes the OpenAI API key variable is always at index 0. If the provider metadata list order changes, the test will fail even if the endpoint remains correct. Prefer locating the variable by variable_key (similar to the WatsonX assertions) to make the test order-independent.

Suggested change
# Check OpenAI primary variable
openai_api_key_var = openai_vars[0]
assert openai_api_key_var["variable_key"] == "OPENAI_API_KEY"
# Check OpenAI primary variable (order-independent)
openai_api_key_var = next((v for v in openai_vars if v["variable_key"] == "OPENAI_API_KEY"), None)
assert openai_api_key_var is not None

Copilot uses AI. Check for mistakes.
@github-actions github-actions bot added enhancement New feature or request and removed enhancement New feature or request labels Jan 26, 2026
Replaces per-variable auto-save with a batch save for all provider variables, improving UX and reducing API calls. Updates input handling to show masked values for secrets, disables save button until all required fields are filled, and provides clearer feedback for configured variables.
@github-actions github-actions bot added enhancement New feature or request and removed enhancement New feature or request labels Jan 26, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/frontend/src/modals/modelProviderModal/components/ModelProvidersContent.tsx (2)

187-249: No‑API‑key activation stores non‑secret values as credentials.
This redacts values like base URLs, making them hard to inspect later. Use the variable’s is_secret flag to choose the type.

🛠️ Proposed fix
@@
-    const variableName =
+    const variableName =
       firstVariable?.variable_key ||
       PROVIDER_VARIABLE_MAPPING[selectedProvider.provider];
+
+    const variableType =
+      firstVariable?.is_secret ? VARIABLE_CATEGORY.CREDENTIAL : VARIABLE_CATEGORY.GLOBAL;
@@
-          type: VARIABLE_CATEGORY.CREDENTIAL,
+          type: variableType,

252-333: Error icon never renders after a failed save.
savingVariableKey is cleared in onError, so the error indicator condition is never true. Track a separate failedVariableKey (or keep the key until the next edit) to show the error state for the right field.

Also applies to: 456-476

🤖 Fix all issues with AI agents
In `@src/backend/base/langflow/services/variable/service.py`:
- Around line 35-53: The code imports get_model_provider_variable_mapping and
assigns provider_mapping = get_model_provider_variable_mapping() but never uses
it; remove the unused import and the provider_mapping assignment to avoid dead
code. Update the import list to drop get_model_provider_variable_mapping and
delete the line that assigns provider_mapping (search for provider_mapping and
get_model_provider_variable_mapping in this file, e.g., in service.py) so only
metadata-related calls (get_model_provider_metadata) and the
var_to_provider/var_to_info logic remain.
- Line 40: The local variable provider_mapping is assigned from
get_model_provider_variable_mapping but never used; remove the unused assignment
(provider_mapping = get_model_provider_variable_mapping()) and, if
get_model_provider_variable_mapping is no longer referenced anywhere in this
module, also remove its import to eliminate the Ruff F841 unused-variable error;
if the mapping was intended to be used, instead add the missing usage where
relevant (e.g., pass it into the function or return it) in the surrounding
service functions.

In `@src/backend/tests/unit/api/v1/test_models_enabled_providers.py`:
- Around line 482-496: The test function
test_backward_compatible_variable_mapping has unused fixture parameters client
and logged_in_headers which trigger Ruff ARG001; remove these unused args from
the function signature so it only relies on the
`@pytest.mark.usefixtures`("active_user") decorator (i.e., change def
test_backward_compatible_variable_mapping(client: AsyncClient,
logged_in_headers): to a signature with no parameters), leaving the body and the
call to get_model_provider_variable_mapping() unchanged.

In
`@src/frontend/src/modals/modelProviderModal/components/ModelProvidersContent.tsx`:
- Around line 44-55: The autosave currently picks the first non-empty entry from
variableValues, causing it to save the wrong field; update the component to
track the last-edited variable key (e.g., add a lastChangedKey ref or state and
update it inside the input onChange handler where variableValues is set) and use
that lastChangedKey when triggering the debounced save instead of scanning
variableValues for the first non-empty key; update logic that reads/writes
savingVariableKey and setSavingVariableKey, the debounce/save handler, and any
places that reset inputs (isUserInputRef usage) so they reference lastChangedKey
to determine which variable to save.

In `@src/lfx/src/lfx/base/models/unified_models.py`:
- Around line 394-441: The _validate_and_get_enabled_providers loop currently
only iterates credential_variables so non-secret required fields (e.g.,
WATSONX_PROJECT_ID/WATSONX_URL) are never found; change the logic to accept a
full variables map (all_variables) rather than only credential_variables, use
get_provider_required_variable_keys(provider) to iterate required keys, call
get_provider_all_variables(provider) or inspect each variable's metadata to
determine is_secret, and for each required var: if is_secret then decrypt via
auth_utils.decrypt_api_key and validate non-empty, otherwise just check
existence and non-empty/strip() without decrypting; keep using
validate_model_provider_key(primary_var_key, primary_api_key) for secret primary
keys and ensure enabled.add(provider) when all required (secret and non-secret)
are present/valid.
🧹 Nitpick comments (1)
src/frontend/src/controllers/API/queries/models/use-get-provider-variables.ts (1)

29-39: Error handling silently masks API failures.

The catch block logs the error but returns an empty object, which may make it difficult for consumers to distinguish between "no providers configured" and "API call failed." Consider propagating the error or exposing an error state so the UI can show appropriate feedback.

♻️ Consider letting the error propagate to React Query
  const getProviderVariablesFn =
    async (): Promise<ProviderVariablesMapping> => {
-     try {
-       const url = `${getURL("MODELS")}/provider-variable-mapping`;
-       const response = await api.get<ProviderVariablesMapping>(url);
-       return response.data;
-     } catch (error) {
-       console.error("Error fetching provider variables mapping:", error);
-       return {};
-     }
+     const url = `${getURL("MODELS")}/provider-variable-mapping`;
+     const response = await api.get<ProviderVariablesMapping>(url);
+     return response.data;
    };

Then consumers can use queryResult.isError and queryResult.error to handle failures appropriately, and you can provide a fallback via the placeholderData option if needed.

Comment on lines +35 to +53
from lfx.base.models.unified_models import (
get_model_provider_metadata,
get_model_provider_variable_mapping,
)

provider_mapping = get_model_provider_variable_mapping()
# Reverse the mapping to go from variable name to provider
var_to_provider = {var_name: provider for provider, var_name in provider_mapping.items()}
# Build var_to_provider from all variables in metadata (not just primary)
var_to_provider = {}
var_to_info = {} # Maps variable_key to its full info (including is_secret)
metadata = get_model_provider_metadata()
for provider, meta in metadata.items():
for var in meta.get("variables", []):
var_key = var.get("variable_key")
if var_key:
var_to_provider[var_key] = provider
var_to_info[var_key] = var
except Exception: # noqa: BLE001
var_to_provider = {}
var_to_info = {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

rg -n 'get_model_provider_variable_mapping' src/backend/base/langflow/services/variable/service.py

Repository: langflow-ai/langflow

Length of output: 191


Remove the unused import and variable assignment.

The get_model_provider_variable_mapping import and the provider_mapping = get_model_provider_variable_mapping() assignment (line 40) should both be removed, as provider_mapping is never used after assignment.

🧰 Tools
🪛 GitHub Actions: Ruff Style Check

[error] 40-40: Ruff/Flake8: F841 Local variable provider_mapping is assigned to but never used.

🪛 GitHub Check: Ruff Style Check (3.13)

[failure] 40-40: Ruff (F841)
src/backend/base/langflow/services/variable/service.py:40:13: F841 Local variable provider_mapping is assigned to but never used

🤖 Prompt for AI Agents
In `@src/backend/base/langflow/services/variable/service.py` around lines 35 - 53,
The code imports get_model_provider_variable_mapping and assigns
provider_mapping = get_model_provider_variable_mapping() but never uses it;
remove the unused import and the provider_mapping assignment to avoid dead code.
Update the import list to drop get_model_provider_variable_mapping and delete
the line that assigns provider_mapping (search for provider_mapping and
get_model_provider_variable_mapping in this file, e.g., in service.py) so only
metadata-related calls (get_model_provider_metadata) and the
var_to_provider/var_to_info logic remain.

get_model_provider_variable_mapping,
)

provider_mapping = get_model_provider_variable_mapping()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove unused variable provider_mapping — causes pipeline failure.

The variable provider_mapping is assigned but never used. This is flagged by Ruff (F841) and is causing the CI pipeline to fail.

🐛 Proposed fix
-            provider_mapping = get_model_provider_variable_mapping()

If get_model_provider_variable_mapping was intended to be used somewhere, that usage is missing. Otherwise, remove the unused import as well if it's no longer needed elsewhere in the file.

📝 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.

Suggested change
provider_mapping = get_model_provider_variable_mapping()
🧰 Tools
🪛 GitHub Actions: Ruff Style Check

[error] 40-40: Ruff/Flake8: F841 Local variable provider_mapping is assigned to but never used.

🪛 GitHub Check: Ruff Style Check (3.13)

[failure] 40-40: Ruff (F841)
src/backend/base/langflow/services/variable/service.py:40:13: F841 Local variable provider_mapping is assigned to but never used

🤖 Prompt for AI Agents
In `@src/backend/base/langflow/services/variable/service.py` at line 40, The local
variable provider_mapping is assigned from get_model_provider_variable_mapping
but never used; remove the unused assignment (provider_mapping =
get_model_provider_variable_mapping()) and, if
get_model_provider_variable_mapping is no longer referenced anywhere in this
module, also remove its import to eliminate the Ruff F841 unused-variable error;
if the mapping was intended to be used, instead add the missing usage where
relevant (e.g., pass it into the function or return it) in the surrounding
service functions.

Comment on lines +482 to +496
@pytest.mark.usefixtures("active_user")
async def test_backward_compatible_variable_mapping(client: AsyncClient, logged_in_headers):
"""Test that get_model_provider_variable_mapping() still returns primary variable (backward compat)."""
from lfx.base.models.unified_models import get_model_provider_variable_mapping

mapping = get_model_provider_variable_mapping()

# Should return dict of provider -> primary variable key
assert isinstance(mapping, dict)
assert mapping.get("OpenAI") == "OPENAI_API_KEY"
assert mapping.get("Anthropic") == "ANTHROPIC_API_KEY"
assert mapping.get("Google Generative AI") == "GOOGLE_API_KEY"
assert mapping.get("Ollama") == "OLLAMA_BASE_URL"
# IBM WatsonX should return primary secret (API key)
assert mapping.get("IBM WatsonX") == "WATSONX_APIKEY"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove unused fixture args to satisfy linting.
client and logged_in_headers are unused here and trigger Ruff (ARG001).

🛠️ Proposed fix
-async def test_backward_compatible_variable_mapping(client: AsyncClient, logged_in_headers):
+async def test_backward_compatible_variable_mapping():
🧰 Tools
🪛 GitHub Check: Ruff Style Check (3.13)

[failure] 483-483: Ruff (ARG001)
src/backend/tests/unit/api/v1/test_models_enabled_providers.py:483:74: ARG001 Unused function argument: logged_in_headers


[failure] 483-483: Ruff (ARG001)
src/backend/tests/unit/api/v1/test_models_enabled_providers.py:483:53: ARG001 Unused function argument: client

🤖 Prompt for AI Agents
In `@src/backend/tests/unit/api/v1/test_models_enabled_providers.py` around lines
482 - 496, The test function test_backward_compatible_variable_mapping has
unused fixture parameters client and logged_in_headers which trigger Ruff
ARG001; remove these unused args from the function signature so it only relies
on the `@pytest.mark.usefixtures`("active_user") decorator (i.e., change def
test_backward_compatible_variable_mapping(client: AsyncClient,
logged_in_headers): to a signature with no parameters), leaving the body and the
call to get_model_provider_variable_mapping() unchanged.

Comment on lines 44 to 55
// State for multiple variable values, keyed by variable_key
const [variableValues, setVariableValues] = useState<Record<string, string>>(
{},
);
const [validationFailed, setValidationFailed] = useState(false);
// Track if API key change came from user typing (vs programmatic reset)
// Used to prevent auto-save from triggering when we clear the input after success
// Track if input change came from user typing (vs programmatic reset)
// Used to prevent auto-save from triggering when we clear inputs after success
const isUserInputRef = useRef(false);
// Track which variable is currently being saved
const [savingVariableKey, setSavingVariableKey] = useState<string | null>(
null,
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Auto‑save targets the first non‑empty variable, not the last edited.
If multiple fields have values, the debounce will keep saving the first one, leaving the just-edited field unsaved unless the user types again. Track the last changed key and save that instead.

🔧 Proposed fix
@@
-  const isUserInputRef = useRef(false);
+  const isUserInputRef = useRef(false);
+  const lastChangedKeyRef = useRef<string | null>(null);
@@
-      const variableToSave = providerVariables.find((v) =>
-        variableValues[v.variable_key]?.trim(),
-      );
+      const variableToSave = providerVariables.find(
+        (v) => v.variable_key === lastChangedKeyRef.current,
+      );
+      const valueToSave = variableToSave
+        ? variableValues[variableToSave.variable_key]?.trim()
+        : "";
       if (variableToSave) {
-        handleSaveVariable(variableToSave);
+        if (valueToSave) handleSaveVariable(variableToSave);
       }
@@
-                        onValueChange={(value) => {
+                        onValueChange={(value) => {
                           isUserInputRef.current = true;
+                          lastChangedKeyRef.current = variable.variable_key;
                           setValidationFailed(false);
@@
-                        onChange={(e) => {
+                        onChange={(e) => {
                           isUserInputRef.current = true;
+                          lastChangedKeyRef.current = variable.variable_key;
                           setValidationFailed(false);

Also applies to: 117-138, 418-455

🤖 Prompt for AI Agents
In
`@src/frontend/src/modals/modelProviderModal/components/ModelProvidersContent.tsx`
around lines 44 - 55, The autosave currently picks the first non-empty entry
from variableValues, causing it to save the wrong field; update the component to
track the last-edited variable key (e.g., add a lastChangedKey ref or state and
update it inside the input onChange handler where variableValues is set) and use
that lastChangedKey when triggering the debounced save instead of scanning
variableValues for the first non-empty key; update logic that reads/writes
savingVariableKey and setSavingVariableKey, the debounce/save handler, and any
places that reset inputs (isUserInputRef usage) so they reference lastChangedKey
to determine which variable to save.

Comment on lines 394 to 441
for provider in provider_variable_map:
# Get all required variable keys for this provider
required_var_keys = get_provider_required_variable_keys(provider)

# Check if all required variables are present
all_required_present = True
primary_var_key = None
primary_api_key = None

for var_key in required_var_keys:
if var_key not in credential_variables:
all_required_present = False
break

# Get the credential variable and decrypt it
credential_var = credential_variables[var_key]
try:
decrypted_value = auth_utils.decrypt_api_key(credential_var.value, settings_service=settings_service)
if not decrypted_value or not decrypted_value.strip():
all_required_present = False
break

# Track the primary secret variable for validation
provider_vars = get_provider_all_variables(provider)
for var in provider_vars:
if var.get("variable_key") == var_key and var.get("is_secret"):
primary_var_key = var_key
primary_api_key = decrypted_value
break
except Exception as e: # noqa: BLE001
logger.debug("Failed to decrypt variable %s for provider %s: %s", var_key, provider, e)
all_required_present = False
break

if not all_required_present:
continue

# Validate the primary API key if we have one
if primary_var_key and primary_api_key:
try:
# Decrypt the API key value
api_key = auth_utils.decrypt_api_key(credential_var.value, settings_service=settings_service)
# Validate the key (this will raise ValueError if invalid)
if api_key and api_key.strip():
validate_model_provider_key(var_name, api_key)
enabled.add(provider)
validate_model_provider_key(primary_var_key, primary_api_key)
enabled.add(provider)
except (ValueError, Exception) as e: # noqa: BLE001
# Key validation failed or decryption failed - don't enable provider
logger.debug("Provider %s validation failed for variable %s: %s", provider, var_name, e)
logger.debug("Provider %s validation failed for variable %s: %s", provider, primary_var_key, e)
elif not required_var_keys:
# Provider has no required variables (like Ollama with optional base URL)
enabled.add(provider)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Required non‑secret variables are ignored during enablement.
_validate_and_get_enabled_providers only receives credential variables, so required non‑secret fields like WATSONX_PROJECT_ID and WATSONX_URL will never be found (they’re stored as GLOBAL in the UI). That means WatsonX will never be marked enabled even when fully configured.

Consider passing all variables into this helper and checking is_secret from metadata to decide whether to decrypt or just validate presence.

🤖 Prompt for AI Agents
In `@src/lfx/src/lfx/base/models/unified_models.py` around lines 394 - 441, The
_validate_and_get_enabled_providers loop currently only iterates
credential_variables so non-secret required fields (e.g.,
WATSONX_PROJECT_ID/WATSONX_URL) are never found; change the logic to accept a
full variables map (all_variables) rather than only credential_variables, use
get_provider_required_variable_keys(provider) to iterate required keys, call
get_provider_all_variables(provider) or inspect each variable's metadata to
determine is_secret, and for each required var: if is_secret then decrypt via
auth_utils.decrypt_api_key and validate non-empty, otherwise just check
existence and non-empty/strip() without decrypting; keep using
validate_model_provider_key(primary_var_key, primary_api_key) for secret primary
keys and ensure enabled.add(provider) when all required (secret and non-secret)
are present/valid.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@src/backend/base/langflow/services/variable/service.py`:
- Around line 35-54: Remove the unused provider_mapping local variable and its
import to satisfy Ruff F841: delete the assignment to provider_mapping and
remove get_model_provider_variable_mapping from the import list (leaving
get_model_provider_metadata), keeping the rest of the logic that builds
var_to_provider and var_to_info intact so functions like
get_model_provider_metadata, var_to_provider, and var_to_info remain unchanged.

In `@src/backend/tests/unit/api/v1/test_models_enabled_providers.py`:
- Around line 483-496: The test function
test_backward_compatible_variable_mapping currently accepts unused fixtures
client and logged_in_headers causing Ruff ARG001; edit the function signature
for test_backward_compatible_variable_mapping to remove the unused parameters so
it has no arguments, leaving the body intact and still importing/using
get_model_provider_variable_mapping to perform the assertions.

In
`@src/frontend/src/controllers/API/queries/models/use-get-provider-variables.ts`:
- Around line 29-38: The getProviderVariablesFn function swallows errors by
catching exceptions and returning an empty object, which prevents React Query
from recognizing failures; modify getProviderVariablesFn (the async function
used in use-get-provider-variables) to let errors propagate by either removing
the try/catch or rethrowing the caught error after logging (e.g., log via
console.error then throw error) so that the API call
(api.get<ProviderVariablesMapping>(...)) will surface errors to React Query
consumers (allowing isError/isLoading states to work correctly).

In `@src/lfx/src/lfx/base/models/unified_models.py`:
- Around line 394-440: The function _validate_and_get_enabled_providers
currently accepts only credential_variables so required non-secret keys
(GENERIC_TYPE) are omitted; change its signature to accept all variables (e.g.,
variables or all_variables) and update call sites that pass credential_variables
to pass the full variable map instead; inside the function, when iterating
required_var_keys (via get_provider_required_variable_keys) use the combined
variables map to locate values, still calling
auth_utils.decrypt_api_key(decrypted_value, settings_service=settings_service)
which handles plaintext, and keep existing logic that finds provider_vars via
get_provider_all_variables and validates with validate_model_provider_key;
ensure callers that previously filtered by CREDENTIAL_TYPE send the unfiltered
collection.

Comment on lines +35 to 54
from lfx.base.models.unified_models import (
get_model_provider_metadata,
get_model_provider_variable_mapping,
)

provider_mapping = get_model_provider_variable_mapping()
# Reverse the mapping to go from variable name to provider
var_to_provider = {var_name: provider for provider, var_name in provider_mapping.items()}
# Build var_to_provider from all variables in metadata (not just primary)
var_to_provider = {}
var_to_info = {} # Maps variable_key to its full info (including is_secret)
metadata = get_model_provider_metadata()
for provider, meta in metadata.items():
for var in meta.get("variables", []):
var_key = var.get("variable_key")
if var_key:
var_to_provider[var_key] = provider
var_to_info[var_key] = var
except Exception: # noqa: BLE001
var_to_provider = {}
var_to_info = {}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove unused provider_mapping to clear Ruff F841.
The local assignment isn’t used and is currently failing the Ruff check. Remove it (and the now-unused import).

🧹 Proposed fix
-            from lfx.base.models.unified_models import (
-                get_model_provider_metadata,
-                get_model_provider_variable_mapping,
-            )
+            from lfx.base.models.unified_models import get_model_provider_metadata
...
-            provider_mapping = get_model_provider_variable_mapping()
🧰 Tools
🪛 GitHub Actions: Ruff Style Check

[error] 40-40: F841 Local variable provider_mapping is assigned to but never used. (From ruff: src/backend/base/langflow/services/variable/service.py:40)

🪛 GitHub Check: Ruff Style Check (3.13)

[failure] 40-40: Ruff (F841)
src/backend/base/langflow/services/variable/service.py:40:13: F841 Local variable provider_mapping is assigned to but never used

🤖 Prompt for AI Agents
In `@src/backend/base/langflow/services/variable/service.py` around lines 35 - 54,
Remove the unused provider_mapping local variable and its import to satisfy Ruff
F841: delete the assignment to provider_mapping and remove
get_model_provider_variable_mapping from the import list (leaving
get_model_provider_metadata), keeping the rest of the logic that builds
var_to_provider and var_to_info intact so functions like
get_model_provider_metadata, var_to_provider, and var_to_info remain unchanged.

Comment on lines +483 to +496
async def test_backward_compatible_variable_mapping(client: AsyncClient, logged_in_headers):
"""Test that get_model_provider_variable_mapping() still returns primary variable (backward compat)."""
from lfx.base.models.unified_models import get_model_provider_variable_mapping

mapping = get_model_provider_variable_mapping()

# Should return dict of provider -> primary variable key
assert isinstance(mapping, dict)
assert mapping.get("OpenAI") == "OPENAI_API_KEY"
assert mapping.get("Anthropic") == "ANTHROPIC_API_KEY"
assert mapping.get("Google Generative AI") == "GOOGLE_API_KEY"
assert mapping.get("Ollama") == "OLLAMA_BASE_URL"
# IBM WatsonX should return primary secret (API key)
assert mapping.get("IBM WatsonX") == "WATSONX_APIKEY"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove unused fixtures to satisfy Ruff (ARG001).

client and logged_in_headers aren’t used in this test; drop them to avoid lint failures.

✅ Suggested fix
-async def test_backward_compatible_variable_mapping(client: AsyncClient, logged_in_headers):
+async def test_backward_compatible_variable_mapping():
🧰 Tools
🪛 GitHub Check: Ruff Style Check (3.13)

[failure] 483-483: Ruff (ARG001)
src/backend/tests/unit/api/v1/test_models_enabled_providers.py:483:74: ARG001 Unused function argument: logged_in_headers


[failure] 483-483: Ruff (ARG001)
src/backend/tests/unit/api/v1/test_models_enabled_providers.py:483:53: ARG001 Unused function argument: client

🤖 Prompt for AI Agents
In `@src/backend/tests/unit/api/v1/test_models_enabled_providers.py` around lines
483 - 496, The test function test_backward_compatible_variable_mapping currently
accepts unused fixtures client and logged_in_headers causing Ruff ARG001; edit
the function signature for test_backward_compatible_variable_mapping to remove
the unused parameters so it has no arguments, leaving the body intact and still
importing/using get_model_provider_variable_mapping to perform the assertions.

Comment on lines +29 to +38
const getProviderVariablesFn =
async (): Promise<ProviderVariablesMapping> => {
try {
const url = `${getURL("MODELS")}/provider-variable-mapping`;
const response = await api.get<ProviderVariablesMapping>(url);
return response.data;
} catch (error) {
console.error("Error fetching provider variables mapping:", error);
return {};
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's view the full file to understand the hook structure
cat -n "src/frontend/src/controllers/API/queries/models/use-get-provider-variables.ts"

Repository: langflow-ai/langflow

Length of output: 2105


🏁 Script executed:

# Search for usages of this hook in the codebase
rg "use-get-provider-variables|useGetProviderVariables" --type ts --type tsx -B 2 -A 3

Repository: langflow-ai/langflow

Length of output: 91


🏁 Script executed:

# Check if this codebase uses React Query (common pattern for query management)
rg "useQuery|useMutation|@tanstack/react-query|react-query" --type ts --type tsx | head -20

Repository: langflow-ai/langflow

Length of output: 91


🏁 Script executed:

# Find the UseRequestProcessor implementation
fd "request-processor" -t f

Repository: langflow-ai/langflow

Length of output: 126


🏁 Script executed:

# Search for usages of useGetProviderVariables with simpler pattern
rg "useGetProviderVariables" -A 5

Repository: langflow-ai/langflow

Length of output: 2900


🏁 Script executed:

# Check how the query processor is implemented
cat -n "src/frontend/src/controllers/services/request-processor.ts"

Repository: langflow-ai/langflow

Length of output: 154


🏁 Script executed:

# Read the correct request-processor file
cat -n "src/frontend/src/controllers/API/services/request-processor.ts"

Repository: langflow-ai/langflow

Length of output: 1874


🏁 Script executed:

# Also check how the consumer hook checks for error state
rg "providerVariablesMapping|isError|error" src/frontend/src/modals/modelProviderModal/components/ModelProvidersContent.tsx -B 2 -A 2

Repository: langflow-ai/langflow

Length of output: 1732


🏁 Script executed:

# Check if ModelProvidersContent uses isError or error from the hook
rg "isError|error.*useGetProviderVariables|useGetProviderVariables.*error" src/frontend/src/modals/modelProviderModal/components/ModelProvidersContent.tsx

Repository: langflow-ai/langflow

Length of output: 46


🏁 Script executed:

# Also check other usages to see if anyone is checking error state
rg "useGetProviderVariables" -A 10 | rg "isError|error"

Repository: langflow-ai/langflow

Length of output: 46


Rethrow errors instead of returning empty object to enable proper error state handling.

The current implementation catches errors and returns {}, which makes React Query treat the request as successful. This masks failures from the UI layer, preventing consumers from detecting and handling errors. Since the hook uses React Query (via UseRequestProcessor), errors should propagate through the query state so consumers can check isError and display error messages when appropriate.

💡 Suggested fix
-      try {
-        const url = `${getURL("MODELS")}/provider-variable-mapping`;
-        const response = await api.get<ProviderVariablesMapping>(url);
-        return response.data;
-      } catch (error) {
-        console.error("Error fetching provider variables mapping:", error);
-        return {};
-      }
+      try {
+        const url = `${getURL("MODELS")}/provider-variable-mapping`;
+        const response = await api.get<ProviderVariablesMapping>(url);
+        return response.data;
+      } catch (error) {
+        console.error("Error fetching provider variables mapping:", error);
+        throw error;
+      }
📝 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.

Suggested change
const getProviderVariablesFn =
async (): Promise<ProviderVariablesMapping> => {
try {
const url = `${getURL("MODELS")}/provider-variable-mapping`;
const response = await api.get<ProviderVariablesMapping>(url);
return response.data;
} catch (error) {
console.error("Error fetching provider variables mapping:", error);
return {};
}
const getProviderVariablesFn =
async (): Promise<ProviderVariablesMapping> => {
try {
const url = `${getURL("MODELS")}/provider-variable-mapping`;
const response = await api.get<ProviderVariablesMapping>(url);
return response.data;
} catch (error) {
console.error("Error fetching provider variables mapping:", error);
throw error;
}
🤖 Prompt for AI Agents
In
`@src/frontend/src/controllers/API/queries/models/use-get-provider-variables.ts`
around lines 29 - 38, The getProviderVariablesFn function swallows errors by
catching exceptions and returning an empty object, which prevents React Query
from recognizing failures; modify getProviderVariablesFn (the async function
used in use-get-provider-variables) to let errors propagate by either removing
the try/catch or rethrowing the caught error after logging (e.g., log via
console.error then throw error) so that the API call
(api.get<ProviderVariablesMapping>(...)) will surface errors to React Query
consumers (allowing isError/isLoading states to work correctly).

Comment on lines 394 to 440
for provider in provider_variable_map:
# Get all required variable keys for this provider
required_var_keys = get_provider_required_variable_keys(provider)

# Check if all required variables are present
all_required_present = True
primary_var_key = None
primary_api_key = None

for var_key in required_var_keys:
if var_key not in credential_variables:
all_required_present = False
break

# Get the credential variable and decrypt it
credential_var = credential_variables[var_key]
try:
decrypted_value = auth_utils.decrypt_api_key(credential_var.value, settings_service=settings_service)
if not decrypted_value or not decrypted_value.strip():
all_required_present = False
break

# Track the primary secret variable for validation
provider_vars = get_provider_all_variables(provider)
for var in provider_vars:
if var.get("variable_key") == var_key and var.get("is_secret"):
primary_var_key = var_key
primary_api_key = decrypted_value
break
except Exception as e: # noqa: BLE001
logger.debug("Failed to decrypt variable %s for provider %s: %s", var_key, provider, e)
all_required_present = False
break

if not all_required_present:
continue

# Validate the primary API key if we have one
if primary_var_key and primary_api_key:
try:
# Decrypt the API key value
api_key = auth_utils.decrypt_api_key(credential_var.value, settings_service=settings_service)
# Validate the key (this will raise ValueError if invalid)
if api_key and api_key.strip():
validate_model_provider_key(var_name, api_key)
enabled.add(provider)
validate_model_provider_key(primary_var_key, primary_api_key)
enabled.add(provider)
except (ValueError, Exception) as e: # noqa: BLE001
# Key validation failed or decryption failed - don't enable provider
logger.debug("Provider %s validation failed for variable %s: %s", provider, var_name, e)
logger.debug("Provider %s validation failed for variable %s: %s", provider, primary_var_key, e)
elif not required_var_keys:
# Provider has no required variables (like Ollama with optional base URL)
enabled.add(provider)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd /tmp && find . -name "unified_models.py" -type f 2>/dev/null | head -5

Repository: langflow-ai/langflow

Length of output: 46


🏁 Script executed:

# Search for the file and verify structure
find . -path "*src/lfx/base/models/unified_models.py" -o -path "*src/lfx/src/lfx/base/models/unified_models.py" 2>/dev/null

Repository: langflow-ai/langflow

Length of output: 111


🏁 Script executed:

# List repository structure to understand layout
ls -la
git ls-files | grep -E "(unified_models|variable|constant)" | head -20

Repository: langflow-ai/langflow

Length of output: 3664


🏁 Script executed:

# Read the function in question
sed -n '380,460p' ./src/lfx/src/lfx/base/models/unified_models.py

Repository: langflow-ai/langflow

Length of output: 3167


🏁 Script executed:

# Find the function definition and calls to _validate_and_get_enabled_providers
rg -n "_validate_and_get_enabled_providers" ./src/lfx/src/lfx/base/models/unified_models.py -B 5 -A 2

Repository: langflow-ai/langflow

Length of output: 1293


🏁 Script executed:

# Check variable service constants and types
cat ./src/backend/base/langflow/services/variable/constants.py

Repository: langflow-ai/langflow

Length of output: 119


🏁 Script executed:

# Search for WatsonX provider configuration
rg -n "WatsonX|watsonx|WATSONX" ./src/lfx/src/lfx/base/models/unified_models.py -i -B 2 -A 2

Repository: langflow-ai/langflow

Length of output: 8001


🏁 Script executed:

# Find get_provider_required_variable_keys function
rg -n "get_provider_required_variable_keys|get_provider_all_variables" ./src/lfx/src/lfx/base/models/unified_models.py -B 5 -A 10

Repository: langflow-ai/langflow

Length of output: 2487


🏁 Script executed:

# Search for model_provider_metadata to see provider definitions
rg -n "model_provider_metadata" ./src/lfx/src/lfx/base/models/unified_models.py -A 100 | head -150

Repository: langflow-ai/langflow

Length of output: 6089


🏁 Script executed:

# Verify how variables are stored by checking variable model/type mapping
rg -n "class.*Variable|CREDENTIAL_TYPE|GENERIC_TYPE" ./src/backend/base/langflow/services/database/models/variable/model.py -B 2 -A 2

Repository: langflow-ai/langflow

Length of output: 1220


🏁 Script executed:

# Check if variable type is determined by is_secret field or something else
rg -n "is_secret|variable.*type|CREDENTIAL" ./src/backend/base/langflow/services/variable/service.py | head -40

Repository: langflow-ai/langflow

Length of output: 1516


🏁 Script executed:

# Check decrypt_api_key function to see if it can handle plain-text GENERIC_TYPE variables
rg -n "def decrypt_api_key" ./src/backend/base/langflow -B 5 -A 15

Repository: langflow-ai/langflow

Length of output: 2254


🏁 Script executed:

# Get the full decrypt_api_key implementation
sed -n '689,730p' ./src/backend/base/langflow/services/auth/utils.py

Repository: langflow-ai/langflow

Length of output: 2049


Ensure required non‑secret variables participate in provider enablement.

_validate_and_get_enabled_providers receives only credential_variables (filtered by type == CREDENTIAL_TYPE at lines 623 and 810). For multi-variable providers like IBM WatsonX, required non-secret keys (Project ID, URL with is_secret=False) are stored as GENERIC_TYPE and never included. Since the function checks for all required variables (line 396 calls get_provider_required_variable_keys), it fails to find the non-secret required variables and the provider never enables.

The decrypt_api_key function already supports plain-text values (falls back gracefully if decryption fails), so passing both credential and generic variables would work. Update the function signature to accept all variables and adjust filtering at call sites (lines 623, 810).

🤖 Prompt for AI Agents
In `@src/lfx/src/lfx/base/models/unified_models.py` around lines 394 - 440, The
function _validate_and_get_enabled_providers currently accepts only
credential_variables so required non-secret keys (GENERIC_TYPE) are omitted;
change its signature to accept all variables (e.g., variables or all_variables)
and update call sites that pass credential_variables to pass the full variable
map instead; inside the function, when iterating required_var_keys (via
get_provider_required_variable_keys) use the combined variables map to locate
values, still calling auth_utils.decrypt_api_key(decrypted_value,
settings_service=settings_service) which handles plaintext, and keep existing
logic that finds provider_vars via get_provider_all_variables and validates with
validate_model_provider_key; ensure callers that previously filtered by
CREDENTIAL_TYPE send the unfiltered collection.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@github-actions github-actions bot removed the enhancement New feature or request label Jan 26, 2026
@github-actions github-actions bot added enhancement New feature or request and removed enhancement New feature or request labels Jan 26, 2026
Centralize MODEL_PROVIDER_METADATA in model_metadata.py and update unified_models.py to import and use it. Refactor provider validation to accept all required variables as a dictionary, supporting providers with multiple required credentials. Simplify and generalize validation logic for all providers.
Centralizes provider-specific parameter mappings in model_metadata.py and introduces get_provider_param_mapping for reuse. Updates unified_models.py to use this mapping, reducing duplication and improving maintainability for model class, API key, and other provider-specific parameters.
@github-actions github-actions bot added enhancement New feature or request and removed enhancement New feature or request labels Jan 26, 2026
Replaces the API key-specific check with a more general configuration requirement, considering all required provider variables (not just secrets). Updates related UI logic and labels to reflect this broader configuration requirement.
@github-actions github-actions bot added enhancement New feature or request and removed enhancement New feature or request labels Jan 26, 2026
@github-actions github-actions bot added enhancement New feature or request and removed enhancement New feature or request labels Jan 26, 2026
@github-actions github-actions bot added enhancement New feature or request and removed enhancement New feature or request labels Jan 26, 2026
Update logic to consider both secret and non-secret variables when determining enabled providers. Refactor to fetch all required provider variables, not just credentials, and ensure correct values are passed for validation. This improves accuracy for providers that require non-secret configuration variables.
@github-actions github-actions bot added enhancement New feature or request and removed enhancement New feature or request labels Jan 26, 2026
Replaces provider-specific field toggling logic in starter project LanguageModelComponent definitions with a call to apply_provider_variable_config_to_build_config. This centralizes provider-specific configuration, improving maintainability and extensibility for new model providers.
@github-actions github-actions bot added enhancement New feature or request and removed enhancement New feature or request labels Jan 26, 2026
@github-actions github-actions bot added enhancement New feature or request and removed enhancement New feature or request labels Jan 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants