Merge remote-tracking branch 'origin/dev' into fix/dev_bugs

This commit is contained in:
Lucas Oliveira 2024-06-14 13:22:14 -03:00
commit 58e0561d4a
15 changed files with 277 additions and 167 deletions

View file

@ -23,14 +23,23 @@ env:
POETRY_VERSION: "1.8.2" POETRY_VERSION: "1.8.2"
jobs: jobs:
docker_build: setup:
name: Build Docker Image
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs:
base_tags: ${{ steps.set-vars.outputs.base_tags }}
main_tags: ${{ steps.set-vars.outputs.main_tags }}
steps:
- uses: actions/checkout@v4
- name: Set Dockerfile and Tags
id: set-vars
run: |
echo "base_tags=langflowai/langflow:base-${{ inputs.version }}" >> $GITHUB_OUTPUT
echo "main_tags=langflowai/langflow:${{ inputs.version }},langflowai/langflow:1.0-alpha" >> $GITHUB_OUTPUT
build_base:
runs-on: ubuntu-latest
needs: setup
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
id: qemu
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub - name: Login to Docker Hub
@ -38,56 +47,54 @@ jobs:
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set Dockerfile and Tags - name: Build and push Base Image
id: set-vars
run: |
if [ "${{ inputs.release_type }}" == "base" ]; then
echo "DOCKERFILE=./docker/build_and_push_base.Dockerfile" >> $GITHUB_ENV
echo "TAGS=langflowai/langflow:base-${{ inputs.version }}" >> $GITHUB_ENV
else
echo "DOCKERFILE=./docker/build_and_push.Dockerfile" >> $GITHUB_ENV
echo "TAGS=langflowai/langflow:${{ inputs.version }},langflowai/langflow:1.0-alpha" >> $GITHUB_ENV
fi
- name: Build and push
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
with: with:
context: . context: .
push: true push: true
platforms: "linux/amd64,linux/arm64/v8" platforms: "linux/amd64,linux/arm64/v8"
file: ${{ env.DOCKERFILE }} file: ./docker/build_and_push_base.Dockerfile
tags: ${{ env.TAGS }} tags: ${{ needs.setup.outputs.base_tags }}
- name: Wait for Docker Hub to propagate
run: sleep 120 build_components:
- name: Build and push (backend) if: ${{ inputs.release_type == 'main' }}
if: ${{ inputs.release_type == 'main' }} runs-on: ubuntu-latest
needs: build_base
strategy:
matrix:
component: [backend, frontend]
include:
- component: backend
dockerfile: ./docker/build_and_push_backend.Dockerfile
tags: langflowai/langflow-backend:${{ inputs.version }},langflowai/langflow-backend:1.0-alpha
- component: frontend
dockerfile: ./docker/frontend/build_and_push_frontend.Dockerfile
tags: langflowai/langflow-frontend:${{ inputs.version }},langflowai/langflow-frontend:1.0-alpha
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push ${{ matrix.component }}
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
with: with:
context: . context: .
push: true push: true
platforms: "linux/amd64,linux/arm64/v8" platforms: "linux/amd64,linux/arm64/v8"
file: ./docker/build_and_push_backend.Dockerfile
build-args: | build-args: |
LANGFLOW_IMAGE=langflowai/langflow:${{ inputs.version }} LANGFLOW_IMAGE=langflowai/langflow:${{ inputs.version }}
tags: | file: ${{ matrix.dockerfile }}
langflowai/langflow-backend:${{ inputs.version }} tags: ${{ matrix.tags }}
langflowai/langflow-backend:1.0-alpha
- name: Build and push (frontend)
if: ${{ inputs.release_type == 'main' }}
uses: docker/build-push-action@v5
with:
context: .
push: true
file: ./docker/frontend/build_and_push_frontend.Dockerfile
platforms: "linux/amd64,linux/arm64/v8"
tags: |
langflowai/langflow-frontend:${{ inputs.version }}
langflowai/langflow-frontend:1.0-alpha
restart-space: restart-space:
name: Restart HuggingFace Spaces name: Restart HuggingFace Spaces
if: ${{ inputs.release_type == 'main' }} if: ${{ inputs.release_type == 'main' }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: docker_build needs: build_base
strategy: strategy:
matrix: matrix:
python-version: python-version:

51
.github/workflows/lint-js.yml vendored Normal file
View file

@ -0,0 +1,51 @@
name: Lint Frontend
on:
pull_request:
paths:
- "src/frontend/**"
env:
NODE_VERSION: "21"
jobs:
run-linters:
name: Run linters
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
id: setup-node
with:
node-version: ${{ env.NODE_VERSION }}
- name: Cache Node.js dependencies
uses: actions/cache@v4
id: npm-cache
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('src/frontend/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install Node.js dependencies
run: |
cd src/frontend
npm ci
if: ${{ steps.setup-node.outputs.cache-hit != 'true' }}
- name: Run linters
uses: wearerequired/lint-action@v1
with:
github_token: ${{ secrets.github_token }}
auto_fix: true
git_email: "gabriel@langflow.org"
# Enable linters
eslint: true
prettier: true
prettier_args: '--write \"{tests,src}/**/*.{js,jsx,ts,tsx,json,md}\" --ignore-path .prettierignore'

View file

@ -1,17 +1,12 @@
name: lint name: Lint Python
on: on:
push:
branches: [main]
paths:
- "poetry.lock"
- "pyproject.toml"
- "src/backend/**"
pull_request: pull_request:
paths: paths:
- "poetry.lock" - "poetry.lock"
- "pyproject.toml" - "pyproject.toml"
- "src/backend/**" - "src/backend/**"
- "tests/**"
env: env:
POETRY_VERSION: "1.8.2" POETRY_VERSION: "1.8.2"
@ -45,6 +40,13 @@ jobs:
path: | path: |
./.mypy_cache ./.mypy_cache
key: ${{ runner.os }}-mypy-${{ hashFiles('**/pyproject.toml') }} key: ${{ runner.os }}-mypy-${{ hashFiles('**/pyproject.toml') }}
- name: Lint check - name: Run linters
run: | uses: wearerequired/lint-action@v1
make lint with:
github_token: ${{ secrets.github_token }}
# Enable linters
git_email: "gabriel@langflow.org"
mypy: true
mypy_args: '--namespace-packages -p "langflow"'
mypy_command_prefix: "poetry run"

14
.github/workflows/matchers/ruff.json vendored Normal file
View file

@ -0,0 +1,14 @@
{
"problemMatcher": [
{
"owner": "ruff",
"pattern": [
{
"regexp": "^(Would reformat): (.+)$",
"message": 1,
"file": 2
}
]
}
]
}

39
.github/workflows/style-check-py.yml vendored Normal file
View file

@ -0,0 +1,39 @@
name: Ruff Style Check
on:
pull_request:
paths:
- "poetry.lock"
- "pyproject.toml"
- "src/backend/**"
- "tests/**"
env:
POETRY_VERSION: "1.8.2"
jobs:
lint:
runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- "3.12"
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
uses: "./.github/actions/poetry_caching"
with:
python-version: ${{ matrix.python-version }}
poetry-version: ${{ env.POETRY_VERSION }}
cache-key: ${{ runner.os }}-poetry-${{ env.POETRY_VERSION }}-${{ hashFiles('**/poetry.lock') }}
- name: Install Python dependencies
run: |
poetry env use ${{ matrix.python-version }}
poetry install
- name: Register problem matcher
run: echo "::add-matcher::.github/workflows/matchers/ruff.json"
- name: Run Ruff
run: poetry run ruff check --output-format=github .
- name: Run Ruff format
run: poetry run ruff format --check .

View file

@ -1,18 +1,5 @@
fail_fast: true fail_fast: true
repos: repos:
- repo: https://github.com/pre-commit/mirrors-eslint
rev: "v9.1.1"
hooks:
- id: eslint
files: \.[jt]sx?$ # *.js, *.jsx, *.ts and *.tsx
types: [file]
args: ["--fix", "--no-warn-ignored"]
additional_dependencies:
- eslint@9.1.1
- eslint-plugin-prettier
- eslint-config-prettier
- prettier
- eslint-plugin-react@latest
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0 rev: v4.1.0
hooks: hooks:
@ -25,16 +12,3 @@ repos:
args: args:
- --fix=lf - --fix=lf
- id: trailing-whitespace - id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.4.2
hooks:
# Run the linter.
- id: ruff
# Python
files: \.py$
types: [file]
# Run the formatter.
- id: ruff-format
files: \.py$
types: [file]

51
poetry.lock generated
View file

@ -471,17 +471,17 @@ files = [
[[package]] [[package]]
name = "boto3" name = "boto3"
version = "1.34.125" version = "1.34.126"
description = "The AWS SDK for Python" description = "The AWS SDK for Python"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "boto3-1.34.125-py3-none-any.whl", hash = "sha256:116d9eb3c26cf313a2e1e44ef704d1f98f9eb18e7628695d07b01b44a8683544"}, {file = "boto3-1.34.126-py3-none-any.whl", hash = "sha256:7f676daef674fe74f34ce4063228eccc6e60c811f574720e31f230296c4bf29a"},
{file = "boto3-1.34.125.tar.gz", hash = "sha256:31c4a5e4d6f9e6116be61ff654b424ddbd1afcdefe0e8b870c4796f9108eb1c6"}, {file = "boto3-1.34.126.tar.gz", hash = "sha256:7e8418b47dd43954a9088d504541bed8a42b6d06e712d02befba134c1c4d7c6d"},
] ]
[package.dependencies] [package.dependencies]
botocore = ">=1.34.125,<1.35.0" botocore = ">=1.34.126,<1.35.0"
jmespath = ">=0.7.1,<2.0.0" jmespath = ">=0.7.1,<2.0.0"
s3transfer = ">=0.10.0,<0.11.0" s3transfer = ">=0.10.0,<0.11.0"
@ -490,13 +490,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
[[package]] [[package]]
name = "botocore" name = "botocore"
version = "1.34.125" version = "1.34.126"
description = "Low-level, data-driven core of boto 3." description = "Low-level, data-driven core of boto 3."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "botocore-1.34.125-py3-none-any.whl", hash = "sha256:71e97e7d2c088f1188ba6976441b5857a5425acd4aaa31b45d13119c9cb86424"}, {file = "botocore-1.34.126-py3-none-any.whl", hash = "sha256:7eff883c638fe30e0b036789df32d851e093d12544615a3b90062b42ac85bdbc"},
{file = "botocore-1.34.125.tar.gz", hash = "sha256:d2882be011ad5b16e7ab4a96360b5b66a0a7e175c1ea06dbf2de473c0a0a33d8"}, {file = "botocore-1.34.126.tar.gz", hash = "sha256:7a8ccb6a7c02456757a984a3a44331b6f51c94cb8b9b287cd045122fd177a4b0"},
] ]
[package.dependencies] [package.dependencies]
@ -4081,22 +4081,25 @@ adal = ["adal (>=1.0.2)"]
[[package]] [[package]]
name = "langchain" name = "langchain"
version = "0.2.3" version = "0.2.4"
description = "Building applications with LLMs through composability" description = "Building applications with LLMs through composability"
optional = false optional = false
python-versions = "<4.0,>=3.8.1" python-versions = "<4.0,>=3.8.1"
files = [ files = [
{file = "langchain-0.2.3-py3-none-any.whl", hash = "sha256:5dc33cd9c8008693d328b7cb698df69073acecc89ad9c2a95f243b3314f8d834"}, {file = "langchain-0.2.4-py3-none-any.whl", hash = "sha256:a04813215c30f944df006031e2febde872af8fab628dcee825d969e07b6cd621"},
{file = "langchain-0.2.3.tar.gz", hash = "sha256:81962cc72cce6515f7bd71e01542727870789bf8b666c6913d85559080c1a201"}, {file = "langchain-0.2.4.tar.gz", hash = "sha256:e704b5b06222d5eba2d02c76f891321d1bac8952ed54e093831b2bdabf99dcd5"},
] ]
[package.dependencies] [package.dependencies]
aiohttp = ">=3.8.3,<4.0.0" aiohttp = ">=3.8.3,<4.0.0"
async-timeout = {version = ">=4.0.0,<5.0.0", markers = "python_version < \"3.11\""} async-timeout = {version = ">=4.0.0,<5.0.0", markers = "python_version < \"3.11\""}
langchain-core = ">=0.2.0,<0.3.0" langchain-core = ">=0.2.6,<0.3.0"
langchain-text-splitters = ">=0.2.0,<0.3.0" langchain-text-splitters = ">=0.2.0,<0.3.0"
langsmith = ">=0.1.17,<0.2.0" langsmith = ">=0.1.17,<0.2.0"
numpy = ">=1,<2" numpy = [
{version = ">=1,<2", markers = "python_version < \"3.12\""},
{version = ">=1.26.0,<2.0.0", markers = "python_version >= \"3.12\""},
]
pydantic = ">=1,<3" pydantic = ">=1,<3"
PyYAML = ">=5.3" PyYAML = ">=5.3"
requests = ">=2,<3" requests = ">=2,<3"
@ -4192,19 +4195,19 @@ tenacity = ">=8.1.0,<9.0.0"
[[package]] [[package]]
name = "langchain-core" name = "langchain-core"
version = "0.2.5" version = "0.2.6"
description = "Building applications with LLMs through composability" description = "Building applications with LLMs through composability"
optional = false optional = false
python-versions = "<4.0,>=3.8.1" python-versions = "<4.0,>=3.8.1"
files = [ files = [
{file = "langchain_core-0.2.5-py3-none-any.whl", hash = "sha256:abe5138f22acff23a079ec538be5268bbf97cf023d51987a0dd474d2a16cae3e"}, {file = "langchain_core-0.2.6-py3-none-any.whl", hash = "sha256:90521c9fc95d8f925e0d2e2d952382676aea6d3f8de611eda1b1810874c31e5d"},
{file = "langchain_core-0.2.5.tar.gz", hash = "sha256:4a5c2f56b22396a63ef4790043660e393adbfa6832b978f023ca996a04b8e752"}, {file = "langchain_core-0.2.6.tar.gz", hash = "sha256:9f0e38da722a558a6e95b6d86de01bd92e84558c47ac8ba599f02eab70a1c873"},
] ]
[package.dependencies] [package.dependencies]
jsonpatch = ">=1.33,<2.0" jsonpatch = ">=1.33,<2.0"
langsmith = ">=0.1.66,<0.2.0" langsmith = ">=0.1.75,<0.2.0"
packaging = ">=23.2,<24.0" packaging = ">=23.2,<25"
pydantic = ">=1,<3" pydantic = ">=1,<3"
PyYAML = ">=5.3" PyYAML = ">=5.3"
tenacity = ">=8.1.0,<9.0.0" tenacity = ">=8.1.0,<9.0.0"
@ -4377,7 +4380,7 @@ six = "*"
[[package]] [[package]]
name = "langflow-base" name = "langflow-base"
version = "0.0.66" version = "0.0.68"
description = "A Python package with a built-in web application" description = "A Python package with a built-in web application"
optional = false optional = false
python-versions = ">=3.10,<3.13" python-versions = ">=3.10,<3.13"
@ -4474,13 +4477,13 @@ requests = ">=2,<3"
[[package]] [[package]]
name = "litellm" name = "litellm"
version = "1.40.9" version = "1.40.12"
description = "Library to easily interface with LLM API providers" description = "Library to easily interface with LLM API providers"
optional = false optional = false
python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8"
files = [ files = [
{file = "litellm-1.40.9-py3-none-any.whl", hash = "sha256:8a4107b9cce114d822de52cbc9bce56f8edc6620f19d0f2257e71834715fb366"}, {file = "litellm-1.40.12-py3-none-any.whl", hash = "sha256:42f1648507f29c60543ba5fdf35d38fc161694da043b201508225bae50d3328c"},
{file = "litellm-1.40.9.tar.gz", hash = "sha256:e0ea07d0b55001a6f60bba2b2ecd72d1f0dca07e656f63937adfdf45f31e5ad7"}, {file = "litellm-1.40.12.tar.gz", hash = "sha256:366bb9c3694b9ef59b3d073bb37ff9ca175ab4090dc187b0a11d2b21db3a6a5d"},
] ]
[package.dependencies] [package.dependencies]
@ -7818,13 +7821,13 @@ websockets = ">=11,<13"
[[package]] [[package]]
name = "redis" name = "redis"
version = "5.0.5" version = "5.0.6"
description = "Python client for Redis database and key-value store" description = "Python client for Redis database and key-value store"
optional = true optional = true
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "redis-5.0.5-py3-none-any.whl", hash = "sha256:30b47d4ebb6b7a0b9b40c1275a19b87bb6f46b3bed82a89012cf56dea4024ada"}, {file = "redis-5.0.6-py3-none-any.whl", hash = "sha256:c0d6d990850c627bbf7be01c5c4cbaadf67b48593e913bb71c9819c30df37eee"},
{file = "redis-5.0.5.tar.gz", hash = "sha256:3417688621acf6ee368dec4a04dd95881be24efd34c79f00d31f62bb528800ae"}, {file = "redis-5.0.6.tar.gz", hash = "sha256:38473cd7c6389ad3e44a91f4c3eaf6bcb8a9f746007f29bf4fb20824ff0b2197"},
] ]
[package.dependencies] [package.dependencies]

View file

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "langflow" name = "langflow"
version = "1.0.0a55" version = "1.0.0a57"
description = "A Python package with a built-in web application" description = "A Python package with a built-in web application"
authors = ["Langflow <contact@langflow.org>"] authors = ["Langflow <contact@langflow.org>"]
maintainers = [ maintainers = [

View file

@ -1,14 +1,13 @@
import warnings
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from typing import Annotated, Coroutine, Optional, Union from typing import Annotated, Coroutine, Optional, Union
from uuid import UUID from uuid import UUID
import warnings
from cryptography.fernet import Fernet from cryptography.fernet import Fernet
from fastapi import Depends, HTTPException, Security, status from fastapi import Depends, HTTPException, Security, status
from fastapi.security import APIKeyHeader, APIKeyQuery, OAuth2PasswordBearer from fastapi.security import APIKeyHeader, APIKeyQuery, OAuth2PasswordBearer
from jose import JWTError, jwt from jose import JWTError, jwt
from loguru import logger
from sqlmodel import Session from sqlmodel import Session
from starlette.websockets import WebSocket from starlette.websockets import WebSocket
@ -92,44 +91,58 @@ async def get_current_user_by_jwt(
) -> User: ) -> User:
settings_service = get_settings_service() settings_service = get_settings_service()
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
if isinstance(token, Coroutine): if isinstance(token, Coroutine):
token = await token token = await token
if settings_service.auth_settings.SECRET_KEY.get_secret_value() is None: secret_key = settings_service.auth_settings.SECRET_KEY.get_secret_value()
raise credentials_exception if secret_key is None:
logger.error("Secret key is not set in settings.")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
# Careful not to leak sensitive information
detail="Authentication failure: Verify authentication settings.",
headers={"WWW-Authenticate": "Bearer"},
)
try: try:
# Ignore warning about datetime.utcnow
with warnings.catch_warnings(): with warnings.catch_warnings():
warnings.simplefilter("ignore") warnings.simplefilter("ignore")
payload = jwt.decode(token, secret_key, algorithms=[settings_service.auth_settings.ALGORITHM])
payload = jwt.decode( user_id: UUID = payload.get("sub")
token, token_type: str = payload.get("type")
settings_service.auth_settings.SECRET_KEY.get_secret_value(),
algorithms=[settings_service.auth_settings.ALGORITHM],
)
user_id: UUID = payload.get("sub") # type: ignore
token_type: str = payload.get("type") # type: ignore
if expires := payload.get("exp", None): if expires := payload.get("exp", None):
expires_datetime = datetime.fromtimestamp(expires, timezone.utc) expires_datetime = datetime.fromtimestamp(expires, timezone.utc)
# TypeError: can't compare offset-naive and offset-aware datetimes
if datetime.now(timezone.utc) > expires_datetime: if datetime.now(timezone.utc) > expires_datetime:
raise credentials_exception logger.info("Token expired for user")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Token has expired.",
headers={"WWW-Authenticate": "Bearer"},
)
if user_id is None or token_type: if user_id is None or token_type:
raise credentials_exception logger.info(f"Invalid token payload. Token type: {token_type}")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid token details.",
headers={"WWW-Authenticate": "Bearer"},
)
except JWTError as e: except JWTError as e:
raise credentials_exception from e logger.error(f"JWT decoding error: {e}")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
) from e
user = get_user_by_id(db, user_id) # type: ignore user = get_user_by_id(db, user_id)
if user is None or not user.is_active: if user is None or not user.is_active:
raise credentials_exception logger.info("User not found or inactive.")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="User not found or is inactive.",
headers={"WWW-Authenticate": "Bearer"},
)
return user return user

View file

@ -6,24 +6,21 @@ from typing import TYPE_CHECKING
import sqlalchemy as sa import sqlalchemy as sa
from alembic import command, util from alembic import command, util
from alembic.config import Config from alembic.config import Config
from loguru import logger
from sqlalchemy import inspect
from sqlalchemy.exc import OperationalError
from sqlalchemy.engine import Engine
from sqlalchemy import event
from sqlmodel import Session, SQLModel, create_engine, select, text
from langflow.services.base import Service from langflow.services.base import Service
from langflow.services.database import models # noqa from langflow.services.database import models # noqa
from langflow.services.database.models.user.crud import get_user_by_username from langflow.services.database.models.user.crud import get_user_by_username
from langflow.services.database.utils import Result, TableResults from langflow.services.database.utils import Result, TableResults
from langflow.services.deps import get_settings_service from langflow.services.deps import get_settings_service
from langflow.services.utils import teardown_superuser from langflow.services.utils import teardown_superuser
from loguru import logger
from sqlalchemy import event, inspect
from sqlalchemy.engine import Engine
from sqlalchemy.exc import OperationalError
from sqlmodel import Session, SQLModel, create_engine, select, text
if TYPE_CHECKING: if TYPE_CHECKING:
from sqlalchemy.engine import Engine
from langflow.services.settings.service import SettingsService from langflow.services.settings.service import SettingsService
from sqlalchemy.engine import Engine
class DatabaseService(Service): class DatabaseService(Service):
@ -48,12 +45,23 @@ class DatabaseService(Service):
connect_args = {"check_same_thread": False} connect_args = {"check_same_thread": False}
else: else:
connect_args = {} connect_args = {}
return create_engine( try:
self.database_url, return create_engine(
connect_args=connect_args, self.database_url,
pool_size=self.settings_service.settings.pool_size, connect_args=connect_args,
max_overflow=self.settings_service.settings.max_overflow, pool_size=self.settings_service.settings.pool_size,
) max_overflow=self.settings_service.settings.max_overflow,
)
except sa.exc.NoSuchModuleError as exc:
# sqlalchemy.exc.NoSuchModuleError: Can't load plugin: sqlalchemy.dialects:postgres
if "postgres" in str(exc) and not self.database_url.startswith("postgresql"):
# https://stackoverflow.com/questions/62688256/sqlalchemy-exc-nosuchmoduleerror-cant-load-plugin-sqlalchemy-dialectspostgre
self.database_url = self.database_url.replace("postgres://", "postgresql://")
logger.warning(
"Fixed postgres dialect in database URL. Replacing postgres:// with postgresql://. To avoid this warning, update the database URL."
)
return self._create_engine()
raise RuntimeError("Error creating database engine") from exc
@event.listens_for(Engine, "connect") @event.listens_for(Engine, "connect")
def on_connection(dbapi_connection, connection_record): def on_connection(dbapi_connection, connection_record):

View file

@ -1158,22 +1158,25 @@ files = [
[[package]] [[package]]
name = "langchain" name = "langchain"
version = "0.2.3" version = "0.2.4"
description = "Building applications with LLMs through composability" description = "Building applications with LLMs through composability"
optional = false optional = false
python-versions = "<4.0,>=3.8.1" python-versions = "<4.0,>=3.8.1"
files = [ files = [
{file = "langchain-0.2.3-py3-none-any.whl", hash = "sha256:5dc33cd9c8008693d328b7cb698df69073acecc89ad9c2a95f243b3314f8d834"}, {file = "langchain-0.2.4-py3-none-any.whl", hash = "sha256:a04813215c30f944df006031e2febde872af8fab628dcee825d969e07b6cd621"},
{file = "langchain-0.2.3.tar.gz", hash = "sha256:81962cc72cce6515f7bd71e01542727870789bf8b666c6913d85559080c1a201"}, {file = "langchain-0.2.4.tar.gz", hash = "sha256:e704b5b06222d5eba2d02c76f891321d1bac8952ed54e093831b2bdabf99dcd5"},
] ]
[package.dependencies] [package.dependencies]
aiohttp = ">=3.8.3,<4.0.0" aiohttp = ">=3.8.3,<4.0.0"
async-timeout = {version = ">=4.0.0,<5.0.0", markers = "python_version < \"3.11\""} async-timeout = {version = ">=4.0.0,<5.0.0", markers = "python_version < \"3.11\""}
langchain-core = ">=0.2.0,<0.3.0" langchain-core = ">=0.2.6,<0.3.0"
langchain-text-splitters = ">=0.2.0,<0.3.0" langchain-text-splitters = ">=0.2.0,<0.3.0"
langsmith = ">=0.1.17,<0.2.0" langsmith = ">=0.1.17,<0.2.0"
numpy = ">=1,<2" numpy = [
{version = ">=1,<2", markers = "python_version < \"3.12\""},
{version = ">=1.26.0,<2.0.0", markers = "python_version >= \"3.12\""},
]
pydantic = ">=1,<3" pydantic = ">=1,<3"
PyYAML = ">=5.3" PyYAML = ">=5.3"
requests = ">=2,<3" requests = ">=2,<3"
@ -1205,19 +1208,19 @@ tenacity = ">=8.1.0,<9.0.0"
[[package]] [[package]]
name = "langchain-core" name = "langchain-core"
version = "0.2.5" version = "0.2.6"
description = "Building applications with LLMs through composability" description = "Building applications with LLMs through composability"
optional = false optional = false
python-versions = "<4.0,>=3.8.1" python-versions = "<4.0,>=3.8.1"
files = [ files = [
{file = "langchain_core-0.2.5-py3-none-any.whl", hash = "sha256:abe5138f22acff23a079ec538be5268bbf97cf023d51987a0dd474d2a16cae3e"}, {file = "langchain_core-0.2.6-py3-none-any.whl", hash = "sha256:90521c9fc95d8f925e0d2e2d952382676aea6d3f8de611eda1b1810874c31e5d"},
{file = "langchain_core-0.2.5.tar.gz", hash = "sha256:4a5c2f56b22396a63ef4790043660e393adbfa6832b978f023ca996a04b8e752"}, {file = "langchain_core-0.2.6.tar.gz", hash = "sha256:9f0e38da722a558a6e95b6d86de01bd92e84558c47ac8ba599f02eab70a1c873"},
] ]
[package.dependencies] [package.dependencies]
jsonpatch = ">=1.33,<2.0" jsonpatch = ">=1.33,<2.0"
langsmith = ">=0.1.66,<0.2.0" langsmith = ">=0.1.75,<0.2.0"
packaging = ">=23.2,<24.0" packaging = ">=23.2,<25"
pydantic = ">=1,<3" pydantic = ">=1,<3"
PyYAML = ">=5.3" PyYAML = ">=5.3"
tenacity = ">=8.1.0,<9.0.0" tenacity = ">=8.1.0,<9.0.0"
@ -1859,13 +1862,13 @@ files = [
[[package]] [[package]]
name = "packaging" name = "packaging"
version = "23.2" version = "24.1"
description = "Core utilities for Python packages" description = "Core utilities for Python packages"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.8"
files = [ files = [
{file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
{file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
] ]
[[package]] [[package]]

View file

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "langflow-base" name = "langflow-base"
version = "0.0.66" version = "0.0.68"
description = "A Python package with a built-in web application" description = "A Python package with a built-in web application"
authors = ["Langflow <contact@langflow.org>"] authors = ["Langflow <contact@langflow.org>"]
maintainers = [ maintainers = [

View file

@ -17,18 +17,18 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
getFolders().then( getFolders().then(
(res) => { (res) => {
const foldersWithoutStarterProjects = res?.filter( const foldersWithoutStarterProjects = res?.filter(
(folder) => folder.name !== STARTER_FOLDER_NAME (folder) => folder.name !== STARTER_FOLDER_NAME,
); );
const starterProjects = res?.find( const starterProjects = res?.find(
(folder) => folder.name === STARTER_FOLDER_NAME (folder) => folder.name === STARTER_FOLDER_NAME,
); );
set({ starterProjectId: starterProjects!.id ?? "" }); set({ starterProjectId: starterProjects!.id ?? "" });
set({ folders: foldersWithoutStarterProjects }); set({ folders: foldersWithoutStarterProjects });
const myCollectionId = res?.find( const myCollectionId = res?.find(
(f) => f.name === DEFAULT_FOLDER (f) => f.name === DEFAULT_FOLDER,
)?.id; )?.id;
set({ myCollectionId }); set({ myCollectionId });
@ -45,7 +45,7 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
set({ folders: [] }); set({ folders: [] });
get().setLoading(false); get().setLoading(false);
reject(error); reject(error);
} },
); );
} }
}); });
@ -54,24 +54,21 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
loading: false, loading: false,
setLoading: (loading) => set(() => ({ loading: loading })), setLoading: (loading) => set(() => ({ loading: loading })),
getFolderById: (id) => { getFolderById: (id) => {
get().setLoadingById(true);
if (id) { if (id) {
getFolderById(id).then( getFolderById(id).then(
(res) => { (res) => {
const setAllFlows = useFlowsManagerStore.getState().setAllFlows; const setAllFlows = useFlowsManagerStore.getState().setAllFlows;
setAllFlows(res.flows); setAllFlows(res.flows);
set({ selectedFolder: res }); set({ selectedFolder: res });
get().setLoadingById(false);
}, },
() => { () => {
get().setLoadingById(false); get().getFoldersApi(true);
} },
); );
} }
}, },
selectedFolder: null, selectedFolder: null,
loadingById: false, loadingById: false,
setLoadingById: (loading) => set(() => ({ loadingById: loading })),
getMyCollectionFolder: () => { getMyCollectionFolder: () => {
const folders = get().folders; const folders = get().folders;
const myCollectionId = folders?.find((f) => f.name === DEFAULT_FOLDER)?.id; const myCollectionId = folders?.find((f) => f.name === DEFAULT_FOLDER)?.id;

View file

@ -8,8 +8,6 @@ export type FoldersStoreType = {
setLoading: (loading: boolean) => void; setLoading: (loading: boolean) => void;
selectedFolder: FolderType | null; selectedFolder: FolderType | null;
getFolderById: (id: string) => void; getFolderById: (id: string) => void;
loadingById: boolean;
setLoadingById: (loading: boolean) => void;
getMyCollectionFolder: () => void; getMyCollectionFolder: () => void;
myCollectionFlows: FolderType | null; myCollectionFlows: FolderType | null;
myCollectionId: string | null; myCollectionId: string | null;

View file

@ -1,6 +1,7 @@
from datetime import datetime from datetime import datetime
import pytest import pytest
from langflow.services.auth.utils import create_super_user, get_password_hash from langflow.services.auth.utils import create_super_user, get_password_hash
from langflow.services.database.models.user import UserUpdate from langflow.services.database.models.user import UserUpdate
from langflow.services.database.models.user.model import User from langflow.services.database.models.user.model import User
@ -95,7 +96,7 @@ def test_data_consistency_after_update(client, active_user, logged_in_headers, s
# Fetch the updated user from the database # Fetch the updated user from the database
response = client.get("/api/v1/users/whoami", headers=logged_in_headers) response = client.get("/api/v1/users/whoami", headers=logged_in_headers)
assert response.status_code == 401, response.json() assert response.status_code == 401, response.json()
assert response.json()["detail"] == "Could not validate credentials" assert response.json()["detail"] == "User not found or is inactive."
def test_data_consistency_after_delete(client, test_user, super_user_headers): def test_data_consistency_after_delete(client, test_user, super_user_headers):