Skip to content

Commit d931fc5

Browse files
committed
Merge branch 'main' into docs-workflow-api
2 parents ee054fd + 8300733 commit d931fc5

43 files changed

Lines changed: 5746 additions & 1414 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.secrets.baseline

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,56 @@
871871
"is_secret": false
872872
}
873873
],
874+
"src/backend/base/langflow/initial_setup/starter_projects/Basic Prompt Chaining.json": [
875+
{
876+
"type": "Hex High Entropy String",
877+
"filename": "src/backend/base/langflow/initial_setup/starter_projects/Basic Prompt Chaining.json",
878+
"hashed_secret": "54ed260e3bc31bc77ee06754dff850981d39a66c",
879+
"is_verified": false,
880+
"line_number": 357,
881+
"is_secret": false
882+
},
883+
{
884+
"type": "Hex High Entropy String",
885+
"filename": "src/backend/base/langflow/initial_setup/starter_projects/Basic Prompt Chaining.json",
886+
"hashed_secret": "35be14614e83fe56d9b2ca1c0e2c2a74890b6889",
887+
"is_verified": false,
888+
"line_number": 625,
889+
"is_secret": false
890+
},
891+
{
892+
"type": "Secret Keyword",
893+
"filename": "src/backend/base/langflow/initial_setup/starter_projects/Basic Prompt Chaining.json",
894+
"hashed_secret": "665b1e3851eefefa3fb878654292f16597d25155",
895+
"is_verified": false,
896+
"line_number": 1400,
897+
"is_secret": false
898+
},
899+
{
900+
"type": "Secret Keyword",
901+
"filename": "src/backend/base/langflow/initial_setup/starter_projects/Basic Prompt Chaining.json",
902+
"hashed_secret": "3f2df46921dd8e2c36e2ce85238705ac0774c74a",
903+
"is_verified": false,
904+
"line_number": 1535,
905+
"is_secret": false
906+
},
907+
{
908+
"type": "Secret Keyword",
909+
"filename": "src/backend/base/langflow/initial_setup/starter_projects/Basic Prompt Chaining.json",
910+
"hashed_secret": "d3d6fe3f7d33d0f4aa28c49544a865982a48a00a",
911+
"is_verified": false,
912+
"line_number": 1595,
913+
"is_secret": false
914+
},
915+
{
916+
"type": "Secret Keyword",
917+
"filename": "src/backend/base/langflow/initial_setup/starter_projects/Basic Prompt Chaining.json",
918+
"hashed_secret": "d4c3d66fd0c38547a3c7a4c6bdc29c36911bc030",
919+
"is_verified": false,
920+
"line_number": 1660,
921+
"is_secret": false
922+
}
923+
],
874924
"src/backend/base/langflow/inputs/input_mixin.py": [
875925
{
876926
"type": "Secret Keyword",

src/backend/base/langflow/api/v1/login.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,9 @@ async def auto_login(response: Response, db: DbSession):
138138
return tokens
139139

140140
raise HTTPException(
141-
status_code=status.HTTP_400_BAD_REQUEST,
141+
status_code=status.HTTP_403_FORBIDDEN,
142142
detail={
143-
"message": "Auto login is disabled. Please enable it in the settings",
143+
"message": "Auto login is disabled.",
144144
"auto_login": False,
145145
},
146146
)

src/backend/base/langflow/api/v1/models.py

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,9 @@ async def get_enabled_providers(
199199
):
200200
"""Get enabled providers for the current user.
201201
202-
Only providers with valid API keys are marked as enabled. This prevents
203-
providers from appearing enabled when they have invalid credentials.
202+
Providers are considered enabled if they have a credential variable stored.
203+
API key validation is performed when credentials are saved, not on every read,
204+
to avoid latency from external API calls.
204205
"""
205206
variable_service = get_variable_service()
206207
try:
@@ -225,35 +226,12 @@ async def get_enabled_providers(
225226
# Get the provider-variable mapping
226227
provider_variable_map = get_model_provider_variable_mapping()
227228

228-
# Build credential_variables dict with objects that have encrypted values
229-
# VariableRead sets value=None for CREDENTIAL_TYPE (via validator), but _validate_and_get_enabled_providers
230-
# needs the encrypted value to decrypt and validate. So we create simple objects with the encrypted value.
231-
credential_variables = {}
232-
233-
for var_name in credential_variable_names:
234-
if var_name and var_name in provider_variable_map.values():
235-
try:
236-
# Get the raw Variable object to access the encrypted value
237-
variable_obj = await variable_service.get_variable_object(
238-
user_id=current_user.id, name=var_name, session=session
239-
)
240-
if variable_obj and variable_obj.value:
241-
# Create a simple object with the encrypted value
242-
# _validate_and_get_enabled_providers only needs .value attribute
243-
class VarWithValue:
244-
def __init__(self, value):
245-
self.value = value
246-
247-
credential_variables[var_name] = VarWithValue(variable_obj.value)
248-
except (ValueError, Exception) as e: # noqa: BLE001
249-
# Variable not found or error accessing it - skip
250-
logger.debug("Skipping variable %s due to error: %s", var_name, e)
251-
continue
252-
253-
# Use shared helper to validate and get enabled providers
254-
from lfx.base.models.unified_models import _validate_and_get_enabled_providers
255-
256-
enabled_providers_set = _validate_and_get_enabled_providers(credential_variables, provider_variable_map)
229+
# Check which providers have credentials stored (no validation - that happens on save)
230+
enabled_providers_set = set()
231+
for provider, var_name in provider_variable_map.items():
232+
if var_name in credential_variable_names:
233+
enabled_providers_set.add(provider)
234+
257235
enabled_providers = list(enabled_providers_set)
258236

259237
# Build provider_status dict for all providers

src/backend/base/langflow/api/v1/users.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,18 @@
2626
async def add_user(
2727
user: UserCreate,
2828
session: DbSession,
29-
current_user: Annotated[User, Depends(get_current_active_superuser)], # noqa: ARG001
3029
) -> User:
3130
"""Add a new user to the database.
3231
33-
Requires superuser authentication to prevent unauthorized account creation.
32+
This endpoint allows public user registration (sign up).
33+
User activation is controlled by the NEW_USER_IS_ACTIVE setting.
3434
"""
35+
settings_service = get_settings_service()
36+
3537
new_user = User.model_validate(user, from_attributes=True)
3638
try:
3739
new_user.password = get_password_hash(user.password)
38-
new_user.is_active = get_settings_service().auth_settings.NEW_USER_IS_ACTIVE
40+
new_user.is_active = settings_service.auth_settings.NEW_USER_IS_ACTIVE
3941
session.add(new_user)
4042
await session.flush()
4143
await session.refresh(new_user)

src/backend/base/langflow/api/v1/variable.py

Lines changed: 10 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
get_model_names_for_provider,
1515
get_provider_from_variable_name,
1616
)
17-
from langflow.services.auth import utils as auth_utils
1817
from langflow.services.database.models.variable.model import VariableCreate, VariableRead, VariableUpdate
1918
from langflow.services.deps import get_variable_service
2019
from langflow.services.variable.constants import CREDENTIAL_TYPE, GENERIC_TYPE
@@ -151,15 +150,10 @@ async def read_variables(
151150
):
152151
"""Read all variables.
153152
154-
Model provider credentials are validated when reading from the database.
155-
If a provider key is invalid, its default_fields are cleared to prevent
156-
the provider from appearing enabled.
153+
Model provider credentials are validated when they are created or updated,
154+
not on every read. This avoids latency from external API calls on read operations.
157155
158-
Each variable in the response includes:
159-
- is_valid: bool | None - True if valid, False if invalid, None if not a provider credential
160-
- validation_error: str | None - Error message if validation failed
161-
162-
Returns a list of variables with validation status for model provider credentials.
156+
Returns a list of variables.
163157
"""
164158
variable_service = get_variable_service()
165159
if not isinstance(variable_service, DatabaseVariableService):
@@ -173,113 +167,18 @@ async def read_variables(
173167
var for var in all_variables if not (var.name and var.name.startswith("__") and var.name.endswith("__"))
174168
]
175169

176-
# Validate model provider credentials and clear default_fields if invalid
177-
# Build dict of credential variables for validation
178-
credential_variables = {var.name: var for var in filtered_variables if var.type == CREDENTIAL_TYPE}
179-
provider_variable_map = get_model_provider_variable_mapping()
180-
181-
# Create reverse mapping: variable_name -> provider
182-
var_to_provider = {var_name: provider for provider, var_name in provider_variable_map.items()}
183-
184-
# Validate each provider credential once and capture both enabled status and error messages
185-
validation_results: dict[
186-
str, tuple[bool, str | None, list[str] | None]
187-
] = {} # var_name -> (is_valid, error, default_fields)
188-
189-
for var_name in provider_variable_map.values():
190-
if var_name in credential_variables:
191-
is_valid = False
192-
error_message = None
193-
variable_obj = None
194-
195-
try:
196-
# Get the raw Variable object to access the encrypted value
197-
variable_obj = await variable_service.get_variable_object(
198-
user_id=current_user.id, name=var_name, session=session
199-
)
200-
if variable_obj and variable_obj.value:
201-
# Decrypt the API key value
202-
from langflow.services.deps import get_settings_service
203-
204-
settings_service = get_settings_service()
205-
decrypted_value = auth_utils.decrypt_api_key(
206-
variable_obj.value, settings_service=settings_service
207-
)
208-
if decrypted_value and decrypted_value.strip():
209-
# Validate the key (this will raise ValueError if invalid)
210-
await asyncio.to_thread(validate_model_provider_key, var_name, decrypted_value)
211-
# Validation passed
212-
is_valid = True
213-
error_message = None
214-
else:
215-
error_message = "API key is empty"
216-
else:
217-
error_message = "Variable value is empty"
218-
except ValueError as e:
219-
# Validation failed - get the error message
220-
error_message = str(e)
221-
except Exception as e: # noqa: BLE001
222-
error_message = f"Validation error: {e!s}"
223-
224-
# Update default_fields based on validation result
225-
updated_default_fields = None
226-
if variable_obj and variable_obj.id:
227-
try:
228-
if is_valid:
229-
# Key is valid - ensure default_fields are set (important for migration)
230-
provider_name = var_to_provider.get(var_name)
231-
expected_default_fields = [provider_name, "api_key"] if provider_name else []
232-
if variable_obj.default_fields != expected_default_fields:
233-
await variable_service.update_variable_fields(
234-
user_id=current_user.id,
235-
variable_id=variable_obj.id,
236-
variable=VariableUpdate(
237-
id=variable_obj.id,
238-
default_fields=expected_default_fields,
239-
),
240-
session=session,
241-
)
242-
updated_default_fields = expected_default_fields
243-
else:
244-
# Key is invalid - clear default_fields
245-
if variable_obj.default_fields:
246-
await variable_service.update_variable_fields(
247-
user_id=current_user.id,
248-
variable_id=variable_obj.id,
249-
variable=VariableUpdate(
250-
id=variable_obj.id,
251-
default_fields=[],
252-
),
253-
session=session,
254-
)
255-
updated_default_fields = []
256-
except Exception: # noqa: BLE001
257-
# Log but don't fail if we can't update
258-
# Use current default_fields if update failed
259-
updated_default_fields = variable_obj.default_fields if variable_obj else None
260-
261-
validation_results[var_name] = (is_valid, error_message, updated_default_fields)
262-
263-
# Set validation status on each variable and update default_fields in response
170+
# Mark model provider credentials - validation status is based on existence
171+
# (actual validation happens on create/update)
264172
for var in filtered_variables:
265173
if var.name and var.name in model_provider_variable_mapping.values() and var.type == CREDENTIAL_TYPE:
266-
result = validation_results.get(var.name)
267-
if result:
268-
is_valid, error_message, updated_default_fields = result
269-
var.is_valid = is_valid
270-
var.validation_error = error_message
271-
# Update default_fields in response to reflect what we set in database
272-
# This is important for migration - valid keys will have default_fields set
273-
if updated_default_fields is not None:
274-
var.default_fields = updated_default_fields
275-
else:
276-
# Variable not found in validation results
277-
var.is_valid = False
278-
var.validation_error = "Variable not found"
174+
# Credential exists and was validated on save
175+
var.is_valid = True
176+
var.validation_error = None
279177
else:
280-
# Not a model provider credential, validation fields remain None
178+
# Not a model provider credential
281179
var.is_valid = None
282180
var.validation_error = None
181+
283182
except Exception as e:
284183
raise HTTPException(status_code=500, detail=str(e)) from e
285184
else:

0 commit comments

Comments
 (0)