-
Notifications
You must be signed in to change notification settings - Fork 8.4k
feat: Add full provider variable metadata and multi-variable support #11446
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
base: main
Are you sure you want to change the base?
Conversation
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.
|
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
WalkthroughThe 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 error, 2 warnings)
✅ Passed checks (4 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
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 |
Codecov Report❌ Patch coverage is ❌ 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. Additional details and impacted files@@ 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
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
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.
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_namemapping with per-providervariables[]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.
| for provider in provider_variable_map: | ||
| # Get all required variable keys for this provider | ||
| required_var_keys = get_provider_required_variable_keys(provider) |
Copilot
AI
Jan 26, 2026
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.
_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).
|
|
||
| return enabled |
Copilot
AI
Jan 26, 2026
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.
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.
| 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) |
| 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); | ||
| } |
Copilot
AI
Jan 26, 2026
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.
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(...).
|
|
||
| const onError = (error: any) => { | ||
| setValidationFailed(true); | ||
| setSavingVariableKey(null); |
Copilot
AI
Jan 26, 2026
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.
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.
| setSavingVariableKey(null); |
| endIcon={ | ||
| isSaving | ||
| ? "LoaderCircle" | ||
| : validationFailed && | ||
| savingVariableKey === variable.variable_key | ||
| ? "X" | ||
| : isConfigured | ||
| ? "Check" | ||
| : undefined | ||
| } |
Copilot
AI
Jan 26, 2026
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.
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.
| 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()} | ||
|
|
||
|
|
Copilot
AI
Jan 26, 2026
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.
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.
| 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() |
| metadata = get_model_provider_metadata() | ||
| return {provider: meta.get("variables", []) for provider, meta in metadata.items()} |
Copilot
AI
Jan 26, 2026
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.
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.
| # Check OpenAI primary variable | ||
| openai_api_key_var = openai_vars[0] | ||
| assert openai_api_key_var["variable_key"] == "OPENAI_API_KEY" |
Copilot
AI
Jan 26, 2026
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.
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.
| # 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 |
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.
…w-ai/langflow into EJ/add-multi-api-support
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: 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’sis_secretflag 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.
savingVariableKeyis cleared inonError, so the error indicator condition is never true. Track a separatefailedVariableKey(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.isErrorandqueryResult.errorto handle failures appropriately, and you can provide a fallback via theplaceholderDataoption if needed.
| 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 = {} |
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:
rg -n 'get_model_provider_variable_mapping' src/backend/base/langflow/services/variable/service.pyRepository: 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() |
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.
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.
| 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.
| @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" |
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.
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.
| // 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, | ||
| ); |
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.
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.
| 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) | ||
|
|
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.
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.
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: 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.
| 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 = {} | ||
|
|
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.
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.
| 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" |
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.
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.
| 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 {}; | ||
| } |
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:
# 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 3Repository: 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 -20Repository: langflow-ai/langflow
Length of output: 91
🏁 Script executed:
# Find the UseRequestProcessor implementation
fd "request-processor" -t fRepository: langflow-ai/langflow
Length of output: 126
🏁 Script executed:
# Search for usages of useGetProviderVariables with simpler pattern
rg "useGetProviderVariables" -A 5Repository: 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 2Repository: 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.tsxRepository: 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.
| 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).
| 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) |
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:
cd /tmp && find . -name "unified_models.py" -type f 2>/dev/null | head -5Repository: 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/nullRepository: 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 -20Repository: 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.pyRepository: 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 2Repository: langflow-ai/langflow
Length of output: 1293
🏁 Script executed:
# Check variable service constants and types
cat ./src/backend/base/langflow/services/variable/constants.pyRepository: 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 2Repository: 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 10Repository: 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 -150Repository: 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 2Repository: 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 -40Repository: 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 15Repository: 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.pyRepository: 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>
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.
…w-ai/langflow into EJ/add-multi-api-support
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.
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.
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.
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.
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
Tests
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.