From 80634f95479164b240e3662f32d3cfb26f6cd4b0 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 13 Jun 2024 09:52:20 -0300 Subject: [PATCH 01/12] =?UTF-8?q?=F0=9F=90=9B=20(index.tsx):=20remove=20de?= =?UTF-8?q?bugger=20statement=20to=20clean=20up=20code=20=E2=99=BB?= =?UTF-8?q?=EF=B8=8F=20(index.tsx):=20remove=20unnecessary=20trailing=20co?= =?UTF-8?q?mmas=20for=20consistency=20=F0=9F=90=9B=20(foldersStore.tsx):?= =?UTF-8?q?=20add=20optional=20chaining=20to=20handle=20potential=20undefi?= =?UTF-8?q?ned=20responses=20=E2=99=BB=EF=B8=8F=20(foldersStore.tsx):=20re?= =?UTF-8?q?move=20unnecessary=20trailing=20commas=20for=20consistency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/src/modals/IOModal/components/chatView/index.tsx | 1 - src/frontend/src/stores/foldersStore.tsx | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/frontend/src/modals/IOModal/components/chatView/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/index.tsx index 736d21a6f..3c3ae0fbf 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/index.tsx @@ -86,7 +86,6 @@ export default function ChatView({ }; } catch (e) { console.error(e); - debugger; return { isSend: false, message: "Error parsing message", diff --git a/src/frontend/src/stores/foldersStore.tsx b/src/frontend/src/stores/foldersStore.tsx index e72610987..b7299d192 100644 --- a/src/frontend/src/stores/foldersStore.tsx +++ b/src/frontend/src/stores/foldersStore.tsx @@ -16,11 +16,11 @@ export const useFolderStore = create((set, get) => ({ get().setLoading(true); getFolders().then( (res) => { - const foldersWithoutStarterProjects = res.filter( + const foldersWithoutStarterProjects = res?.filter( (folder) => folder.name !== STARTER_FOLDER_NAME, ); - const starterProjects = res.find( + const starterProjects = res?.find( (folder) => folder.name === STARTER_FOLDER_NAME, ); From 7313e5807b73dcfd87d0da29b9277ddfd59a7b30 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 13 Jun 2024 16:08:58 -0300 Subject: [PATCH 02/12] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20(foldersStore.tsx):?= =?UTF-8?q?=20remove=20redundant=20loadingById=20state=20and=20setLoadingB?= =?UTF-8?q?yId=20function=20=F0=9F=90=9B=20(foldersStore.tsx):=20fix=20fal?= =?UTF-8?q?lback=20to=20getFoldersApi=20on=20getFolderById=20failure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/src/stores/foldersStore.tsx | 15 ++++++--------- src/frontend/src/types/zustand/folders/index.ts | 2 -- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/frontend/src/stores/foldersStore.tsx b/src/frontend/src/stores/foldersStore.tsx index 190fc3212..ce858c3be 100644 --- a/src/frontend/src/stores/foldersStore.tsx +++ b/src/frontend/src/stores/foldersStore.tsx @@ -17,18 +17,18 @@ export const useFolderStore = create((set, get) => ({ getFolders().then( (res) => { const foldersWithoutStarterProjects = res?.filter( - (folder) => folder.name !== STARTER_FOLDER_NAME + (folder) => folder.name !== STARTER_FOLDER_NAME, ); const starterProjects = res?.find( - (folder) => folder.name === STARTER_FOLDER_NAME + (folder) => folder.name === STARTER_FOLDER_NAME, ); set({ starterProjectId: starterProjects!.id ?? "" }); set({ folders: foldersWithoutStarterProjects }); const myCollectionId = res?.find( - (f) => f.name === DEFAULT_FOLDER + (f) => f.name === DEFAULT_FOLDER, )?.id; set({ myCollectionId }); @@ -45,7 +45,7 @@ export const useFolderStore = create((set, get) => ({ set({ folders: [] }); get().setLoading(false); reject(error); - } + }, ); } }); @@ -54,24 +54,21 @@ export const useFolderStore = create((set, get) => ({ loading: false, setLoading: (loading) => set(() => ({ loading: loading })), getFolderById: (id) => { - get().setLoadingById(true); if (id) { getFolderById(id).then( (res) => { const setAllFlows = useFlowsManagerStore.getState().setAllFlows; setAllFlows(res.flows); set({ selectedFolder: res }); - get().setLoadingById(false); }, () => { - get().setLoadingById(false); - } + get().getFoldersApi(true); + }, ); } }, selectedFolder: null, loadingById: false, - setLoadingById: (loading) => set(() => ({ loadingById: loading })), getMyCollectionFolder: () => { const folders = get().folders; const myCollectionId = folders?.find((f) => f.name === DEFAULT_FOLDER)?.id; diff --git a/src/frontend/src/types/zustand/folders/index.ts b/src/frontend/src/types/zustand/folders/index.ts index 2c5b2b96b..5e41677b2 100644 --- a/src/frontend/src/types/zustand/folders/index.ts +++ b/src/frontend/src/types/zustand/folders/index.ts @@ -8,8 +8,6 @@ export type FoldersStoreType = { setLoading: (loading: boolean) => void; selectedFolder: FolderType | null; getFolderById: (id: string) => void; - loadingById: boolean; - setLoadingById: (loading: boolean) => void; getMyCollectionFolder: () => void; myCollectionFlows: FolderType | null; myCollectionId: string | null; From 5de41ecb3a924c3f638380558ccf7d9b664d7ce9 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 13 Jun 2024 13:36:48 -0700 Subject: [PATCH 03/12] =?UTF-8?q?=F0=9F=90=9B=20(service.py):=20Fix=20hand?= =?UTF-8?q?ling=20of=20postgres=20dialect=20in=20database=20URL=20to=20pre?= =?UTF-8?q?vent=20NoSuchModuleError=20and=20provide=20a=20warning=20messag?= =?UTF-8?q?e=20for=20users=20to=20update=20the=20database=20URL.=20(#2161)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../langflow/services/database/service.py | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/backend/base/langflow/services/database/service.py b/src/backend/base/langflow/services/database/service.py index db5b63c70..ceeaf3e38 100644 --- a/src/backend/base/langflow/services/database/service.py +++ b/src/backend/base/langflow/services/database/service.py @@ -6,24 +6,21 @@ from typing import TYPE_CHECKING import sqlalchemy as sa from alembic import command, util 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.database import models # noqa from langflow.services.database.models.user.crud import get_user_by_username from langflow.services.database.utils import Result, TableResults from langflow.services.deps import get_settings_service 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: - from sqlalchemy.engine import Engine - from langflow.services.settings.service import SettingsService + from sqlalchemy.engine import Engine class DatabaseService(Service): @@ -48,12 +45,23 @@ class DatabaseService(Service): connect_args = {"check_same_thread": False} else: connect_args = {} - return create_engine( - self.database_url, - connect_args=connect_args, - pool_size=self.settings_service.settings.pool_size, - max_overflow=self.settings_service.settings.max_overflow, - ) + try: + return create_engine( + self.database_url, + connect_args=connect_args, + 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") def on_connection(dbapi_connection, connection_record): From d921865e4584a0593284c681cdeaa7ceab1f1bbd Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Thu, 13 Jun 2024 18:33:48 -0300 Subject: [PATCH 04/12] chore: Update dependencies to latest versions --- poetry.lock | 28 ++++++++++++++-------------- pyproject.toml | 2 +- src/backend/base/pyproject.toml | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/poetry.lock b/poetry.lock index f248ce18b..b39fc9d0b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -471,17 +471,17 @@ files = [ [[package]] name = "boto3" -version = "1.34.125" +version = "1.34.126" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.34.125-py3-none-any.whl", hash = "sha256:116d9eb3c26cf313a2e1e44ef704d1f98f9eb18e7628695d07b01b44a8683544"}, - {file = "boto3-1.34.125.tar.gz", hash = "sha256:31c4a5e4d6f9e6116be61ff654b424ddbd1afcdefe0e8b870c4796f9108eb1c6"}, + {file = "boto3-1.34.126-py3-none-any.whl", hash = "sha256:7f676daef674fe74f34ce4063228eccc6e60c811f574720e31f230296c4bf29a"}, + {file = "boto3-1.34.126.tar.gz", hash = "sha256:7e8418b47dd43954a9088d504541bed8a42b6d06e712d02befba134c1c4d7c6d"}, ] [package.dependencies] -botocore = ">=1.34.125,<1.35.0" +botocore = ">=1.34.126,<1.35.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -490,13 +490,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.125" +version = "1.34.126" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.34.125-py3-none-any.whl", hash = "sha256:71e97e7d2c088f1188ba6976441b5857a5425acd4aaa31b45d13119c9cb86424"}, - {file = "botocore-1.34.125.tar.gz", hash = "sha256:d2882be011ad5b16e7ab4a96360b5b66a0a7e175c1ea06dbf2de473c0a0a33d8"}, + {file = "botocore-1.34.126-py3-none-any.whl", hash = "sha256:7eff883c638fe30e0b036789df32d851e093d12544615a3b90062b42ac85bdbc"}, + {file = "botocore-1.34.126.tar.gz", hash = "sha256:7a8ccb6a7c02456757a984a3a44331b6f51c94cb8b9b287cd045122fd177a4b0"}, ] [package.dependencies] @@ -4377,7 +4377,7 @@ six = "*" [[package]] name = "langflow-base" -version = "0.0.66" +version = "0.0.67" description = "A Python package with a built-in web application" optional = false python-versions = ">=3.10,<3.13" @@ -4474,13 +4474,13 @@ requests = ">=2,<3" [[package]] name = "litellm" -version = "1.40.9" +version = "1.40.10" description = "Library to easily interface with LLM API providers" optional = false python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" files = [ - {file = "litellm-1.40.9-py3-none-any.whl", hash = "sha256:8a4107b9cce114d822de52cbc9bce56f8edc6620f19d0f2257e71834715fb366"}, - {file = "litellm-1.40.9.tar.gz", hash = "sha256:e0ea07d0b55001a6f60bba2b2ecd72d1f0dca07e656f63937adfdf45f31e5ad7"}, + {file = "litellm-1.40.10-py3-none-any.whl", hash = "sha256:46b77c49593f4e5e7bd9c1291f5896549b6ff6ebdad457af3ce0f4937bcfc17d"}, + {file = "litellm-1.40.10.tar.gz", hash = "sha256:cdfc86f1de60491cd85155d98d64beec019c35dd0d8c8784037655cb7c0bdddc"}, ] [package.dependencies] @@ -7818,13 +7818,13 @@ websockets = ">=11,<13" [[package]] name = "redis" -version = "5.0.5" +version = "5.0.6" description = "Python client for Redis database and key-value store" optional = true python-versions = ">=3.7" files = [ - {file = "redis-5.0.5-py3-none-any.whl", hash = "sha256:30b47d4ebb6b7a0b9b40c1275a19b87bb6f46b3bed82a89012cf56dea4024ada"}, - {file = "redis-5.0.5.tar.gz", hash = "sha256:3417688621acf6ee368dec4a04dd95881be24efd34c79f00d31f62bb528800ae"}, + {file = "redis-5.0.6-py3-none-any.whl", hash = "sha256:c0d6d990850c627bbf7be01c5c4cbaadf67b48593e913bb71c9819c30df37eee"}, + {file = "redis-5.0.6.tar.gz", hash = "sha256:38473cd7c6389ad3e44a91f4c3eaf6bcb8a9f746007f29bf4fb20824ff0b2197"}, ] [package.dependencies] diff --git a/pyproject.toml b/pyproject.toml index 3b660249d..e1158fe30 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "langflow" -version = "1.0.0a55" +version = "1.0.0a56" description = "A Python package with a built-in web application" authors = ["Langflow "] maintainers = [ diff --git a/src/backend/base/pyproject.toml b/src/backend/base/pyproject.toml index 03f3f70f1..0dd32d331 100644 --- a/src/backend/base/pyproject.toml +++ b/src/backend/base/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "langflow-base" -version = "0.0.66" +version = "0.0.67" description = "A Python package with a built-in web application" authors = ["Langflow "] maintainers = [ From c818c4759411567d7041d0fda788b1cca565f9b3 Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Thu, 13 Jun 2024 18:47:37 -0300 Subject: [PATCH 05/12] chore: Update Docker build workflow to include base and component images --- .github/workflows/docker-build.yml | 85 ++++++++++++++++-------------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index f63920703..f2492eacf 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -23,14 +23,24 @@ env: POETRY_VERSION: "1.8.2" jobs: - docker_build: - name: Build Docker Image + setup: 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 "::set-output name=base_tags::langflowai/langflow:base-${{ inputs.version }}" + echo "::set-output name=main_tags::langflowai/langflow:${{ inputs.version }},langflowai/langflow:1.0-alpha" + + build_base: + runs-on: ubuntu-latest + needs: setup steps: - uses: actions/checkout@v4 - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - id: qemu - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to Docker Hub @@ -38,56 +48,51 @@ jobs: with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Set Dockerfile and Tags - 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 + - name: Build and push Base Image uses: docker/build-push-action@v5 with: context: . push: true platforms: "linux/amd64,linux/arm64/v8" - file: ${{ env.DOCKERFILE }} - tags: ${{ env.TAGS }} - - name: Wait for Docker Hub to propagate - run: sleep 120 - - name: Build and push (backend) - if: ${{ inputs.release_type == 'main' }} + file: ./docker/build_and_push_base.Dockerfile + tags: ${{ needs.setup.outputs.base_tags }} + + build_components: + 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 with: context: . push: true platforms: "linux/amd64,linux/arm64/v8" - file: ./docker/build_and_push_backend.Dockerfile - build-args: | - LANGFLOW_IMAGE=langflowai/langflow:${{ inputs.version }} - tags: | - langflowai/langflow-backend:${{ inputs.version }} - 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 + file: ${{ matrix.dockerfile }} + tags: ${{ matrix.tags }} restart-space: name: Restart HuggingFace Spaces if: ${{ inputs.release_type == 'main' }} runs-on: ubuntu-latest - needs: docker_build + needs: build_base strategy: matrix: python-version: From 05ceb29dec511f4ebfa4024006df9e27cf849ed0 Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Thu, 13 Jun 2024 18:48:43 -0300 Subject: [PATCH 06/12] chore: Update Docker build workflow to include base and component images --- .github/workflows/docker-build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index f2492eacf..e4f53bac4 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -58,6 +58,7 @@ jobs: tags: ${{ needs.setup.outputs.base_tags }} build_components: + if: ${{ inputs.release_type == 'main' }} runs-on: ubuntu-latest needs: build_base strategy: From 5f87e10127512b656adc36fc3d21ba2ca68c7adf Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Fri, 14 Jun 2024 08:19:38 -0300 Subject: [PATCH 07/12] =?UTF-8?q?=F0=9F=94=A7=20(docker-build.yml):=20upda?= =?UTF-8?q?te=20setting=20output=20variables=20to=20use=20a=20file=20for?= =?UTF-8?q?=20better=20readability=20and=20maintainability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/docker-build.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index e4f53bac4..4741f4117 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -33,9 +33,8 @@ jobs: - name: Set Dockerfile and Tags id: set-vars run: | - echo "::set-output name=base_tags::langflowai/langflow:base-${{ inputs.version }}" - echo "::set-output name=main_tags::langflowai/langflow:${{ inputs.version }},langflowai/langflow:1.0-alpha" - + 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 From 5300b9cc08c81c1b7f08004e33057c39a60c0bd3 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 14 Jun 2024 04:24:52 -0700 Subject: [PATCH 08/12] Improve error handling and logging in get_current_user_by_jwt function (#2165) chore: Improve error handling and logging in get_current_user_by_jwt function --- .../base/langflow/services/auth/utils.py | 65 +++++++++++-------- tests/test_user.py | 3 +- 2 files changed, 41 insertions(+), 27 deletions(-) diff --git a/src/backend/base/langflow/services/auth/utils.py b/src/backend/base/langflow/services/auth/utils.py index f62d3ce4f..ebea58e7a 100644 --- a/src/backend/base/langflow/services/auth/utils.py +++ b/src/backend/base/langflow/services/auth/utils.py @@ -1,14 +1,13 @@ +import warnings from datetime import datetime, timedelta, timezone from typing import Annotated, Coroutine, Optional, Union from uuid import UUID -import warnings from cryptography.fernet import Fernet from fastapi import Depends, HTTPException, Security, status from fastapi.security import APIKeyHeader, APIKeyQuery, OAuth2PasswordBearer - - from jose import JWTError, jwt +from loguru import logger from sqlmodel import Session from starlette.websockets import WebSocket @@ -92,44 +91,58 @@ async def get_current_user_by_jwt( ) -> User: 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): token = await token - if settings_service.auth_settings.SECRET_KEY.get_secret_value() is None: - raise credentials_exception + secret_key = settings_service.auth_settings.SECRET_KEY.get_secret_value() + 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: - # Ignore warning about datetime.utcnow with warnings.catch_warnings(): warnings.simplefilter("ignore") - - payload = jwt.decode( - token, - 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 + payload = jwt.decode(token, secret_key, algorithms=[settings_service.auth_settings.ALGORITHM]) + user_id: UUID = payload.get("sub") + token_type: str = payload.get("type") if expires := payload.get("exp", None): expires_datetime = datetime.fromtimestamp(expires, timezone.utc) - # TypeError: can't compare offset-naive and offset-aware datetimes if datetime.now(timezone.utc) > expires_datetime: - raise credentials_exception + logger.info("Token expired for user ID: %s", user_id) + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Token has expired.", + headers={"WWW-Authenticate": "Bearer"}, + ) if user_id is None or token_type: - raise credentials_exception + logger.info("Invalid token payload: %s", payload) + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Invalid token details.", + headers={"WWW-Authenticate": "Bearer"}, + ) except JWTError as e: - raise credentials_exception from e + logger.error("JWT decoding error: %s", str(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: - raise credentials_exception + logger.info("User not found or inactive: %s", user_id) + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="User not found or is inactive.", + headers={"WWW-Authenticate": "Bearer"}, + ) return user diff --git a/tests/test_user.py b/tests/test_user.py index 5f894e801..759ce6aaf 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -1,6 +1,7 @@ from datetime import datetime import pytest + 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.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 response = client.get("/api/v1/users/whoami", headers=logged_in_headers) 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): From 3738a32b92b4a9c45f6df1e8496f23d3b3cba1a2 Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Fri, 14 Jun 2024 08:36:28 -0300 Subject: [PATCH 09/12] chore: Update Docker build workflow to include LANGFLOW_IMAGE build argument --- .github/workflows/docker-build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 4741f4117..c5ea5f6f7 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -85,6 +85,8 @@ jobs: context: . push: true platforms: "linux/amd64,linux/arm64/v8" + build-args: | + LANGFLOW_IMAGE=langflowai/langflow:${{ inputs.version }} file: ${{ matrix.dockerfile }} tags: ${{ matrix.tags }} From 38c721f38734651392778b475716e65ca806c83e Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Fri, 14 Jun 2024 08:39:42 -0300 Subject: [PATCH 10/12] chore: Bump version of langflow and langflow-base packages --- pyproject.toml | 2 +- src/backend/base/pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e1158fe30..c6fbaff06 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "langflow" -version = "1.0.0a56" +version = "1.0.0a57" description = "A Python package with a built-in web application" authors = ["Langflow "] maintainers = [ diff --git a/src/backend/base/pyproject.toml b/src/backend/base/pyproject.toml index 0dd32d331..a833470a2 100644 --- a/src/backend/base/pyproject.toml +++ b/src/backend/base/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "langflow-base" -version = "0.0.67" +version = "0.0.68" description = "A Python package with a built-in web application" authors = ["Langflow "] maintainers = [ From a7e83baa35323c81477edf090f8af092262a2927 Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Fri, 14 Jun 2024 08:53:15 -0300 Subject: [PATCH 11/12] chore: Bump langchain and langchain-core versions to 0.2.4 and 0.2.6 respectively --- poetry.lock | 31 +++++++++++++++++-------------- src/backend/base/poetry.lock | 31 +++++++++++++++++-------------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/poetry.lock b/poetry.lock index b39fc9d0b..31864a0c5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -4081,22 +4081,25 @@ adal = ["adal (>=1.0.2)"] [[package]] name = "langchain" -version = "0.2.3" +version = "0.2.4" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain-0.2.3-py3-none-any.whl", hash = "sha256:5dc33cd9c8008693d328b7cb698df69073acecc89ad9c2a95f243b3314f8d834"}, - {file = "langchain-0.2.3.tar.gz", hash = "sha256:81962cc72cce6515f7bd71e01542727870789bf8b666c6913d85559080c1a201"}, + {file = "langchain-0.2.4-py3-none-any.whl", hash = "sha256:a04813215c30f944df006031e2febde872af8fab628dcee825d969e07b6cd621"}, + {file = "langchain-0.2.4.tar.gz", hash = "sha256:e704b5b06222d5eba2d02c76f891321d1bac8952ed54e093831b2bdabf99dcd5"}, ] [package.dependencies] aiohttp = ">=3.8.3,<4.0.0" 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" 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" PyYAML = ">=5.3" requests = ">=2,<3" @@ -4192,19 +4195,19 @@ tenacity = ">=8.1.0,<9.0.0" [[package]] name = "langchain-core" -version = "0.2.5" +version = "0.2.6" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_core-0.2.5-py3-none-any.whl", hash = "sha256:abe5138f22acff23a079ec538be5268bbf97cf023d51987a0dd474d2a16cae3e"}, - {file = "langchain_core-0.2.5.tar.gz", hash = "sha256:4a5c2f56b22396a63ef4790043660e393adbfa6832b978f023ca996a04b8e752"}, + {file = "langchain_core-0.2.6-py3-none-any.whl", hash = "sha256:90521c9fc95d8f925e0d2e2d952382676aea6d3f8de611eda1b1810874c31e5d"}, + {file = "langchain_core-0.2.6.tar.gz", hash = "sha256:9f0e38da722a558a6e95b6d86de01bd92e84558c47ac8ba599f02eab70a1c873"}, ] [package.dependencies] jsonpatch = ">=1.33,<2.0" -langsmith = ">=0.1.66,<0.2.0" -packaging = ">=23.2,<24.0" +langsmith = ">=0.1.75,<0.2.0" +packaging = ">=23.2,<25" pydantic = ">=1,<3" PyYAML = ">=5.3" tenacity = ">=8.1.0,<9.0.0" @@ -4377,7 +4380,7 @@ six = "*" [[package]] name = "langflow-base" -version = "0.0.67" +version = "0.0.68" description = "A Python package with a built-in web application" optional = false python-versions = ">=3.10,<3.13" @@ -4474,13 +4477,13 @@ requests = ">=2,<3" [[package]] name = "litellm" -version = "1.40.10" +version = "1.40.12" description = "Library to easily interface with LLM API providers" optional = false python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" files = [ - {file = "litellm-1.40.10-py3-none-any.whl", hash = "sha256:46b77c49593f4e5e7bd9c1291f5896549b6ff6ebdad457af3ce0f4937bcfc17d"}, - {file = "litellm-1.40.10.tar.gz", hash = "sha256:cdfc86f1de60491cd85155d98d64beec019c35dd0d8c8784037655cb7c0bdddc"}, + {file = "litellm-1.40.12-py3-none-any.whl", hash = "sha256:42f1648507f29c60543ba5fdf35d38fc161694da043b201508225bae50d3328c"}, + {file = "litellm-1.40.12.tar.gz", hash = "sha256:366bb9c3694b9ef59b3d073bb37ff9ca175ab4090dc187b0a11d2b21db3a6a5d"}, ] [package.dependencies] diff --git a/src/backend/base/poetry.lock b/src/backend/base/poetry.lock index ab30d8f08..8f57c7d7b 100644 --- a/src/backend/base/poetry.lock +++ b/src/backend/base/poetry.lock @@ -1158,22 +1158,25 @@ files = [ [[package]] name = "langchain" -version = "0.2.3" +version = "0.2.4" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain-0.2.3-py3-none-any.whl", hash = "sha256:5dc33cd9c8008693d328b7cb698df69073acecc89ad9c2a95f243b3314f8d834"}, - {file = "langchain-0.2.3.tar.gz", hash = "sha256:81962cc72cce6515f7bd71e01542727870789bf8b666c6913d85559080c1a201"}, + {file = "langchain-0.2.4-py3-none-any.whl", hash = "sha256:a04813215c30f944df006031e2febde872af8fab628dcee825d969e07b6cd621"}, + {file = "langchain-0.2.4.tar.gz", hash = "sha256:e704b5b06222d5eba2d02c76f891321d1bac8952ed54e093831b2bdabf99dcd5"}, ] [package.dependencies] aiohttp = ">=3.8.3,<4.0.0" 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" 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" PyYAML = ">=5.3" requests = ">=2,<3" @@ -1205,19 +1208,19 @@ tenacity = ">=8.1.0,<9.0.0" [[package]] name = "langchain-core" -version = "0.2.5" +version = "0.2.6" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_core-0.2.5-py3-none-any.whl", hash = "sha256:abe5138f22acff23a079ec538be5268bbf97cf023d51987a0dd474d2a16cae3e"}, - {file = "langchain_core-0.2.5.tar.gz", hash = "sha256:4a5c2f56b22396a63ef4790043660e393adbfa6832b978f023ca996a04b8e752"}, + {file = "langchain_core-0.2.6-py3-none-any.whl", hash = "sha256:90521c9fc95d8f925e0d2e2d952382676aea6d3f8de611eda1b1810874c31e5d"}, + {file = "langchain_core-0.2.6.tar.gz", hash = "sha256:9f0e38da722a558a6e95b6d86de01bd92e84558c47ac8ba599f02eab70a1c873"}, ] [package.dependencies] jsonpatch = ">=1.33,<2.0" -langsmith = ">=0.1.66,<0.2.0" -packaging = ">=23.2,<24.0" +langsmith = ">=0.1.75,<0.2.0" +packaging = ">=23.2,<25" pydantic = ">=1,<3" PyYAML = ">=5.3" tenacity = ">=8.1.0,<9.0.0" @@ -1859,13 +1862,13 @@ files = [ [[package]] name = "packaging" -version = "23.2" +version = "24.1" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] From df634de53f56af5e1fe8838685518c2ca8e6b095 Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Fri, 14 Jun 2024 08:55:33 -0300 Subject: [PATCH 12/12] chore: Improve error handling and logging in get_current_user_by_jwt function --- src/backend/base/langflow/services/auth/utils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/backend/base/langflow/services/auth/utils.py b/src/backend/base/langflow/services/auth/utils.py index ebea58e7a..d0ece7562 100644 --- a/src/backend/base/langflow/services/auth/utils.py +++ b/src/backend/base/langflow/services/auth/utils.py @@ -113,7 +113,7 @@ async def get_current_user_by_jwt( if expires := payload.get("exp", None): expires_datetime = datetime.fromtimestamp(expires, timezone.utc) if datetime.now(timezone.utc) > expires_datetime: - logger.info("Token expired for user ID: %s", user_id) + logger.info("Token expired for user") raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Token has expired.", @@ -121,14 +121,14 @@ async def get_current_user_by_jwt( ) if user_id is None or token_type: - logger.info("Invalid token payload: %s", payload) + 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: - logger.error("JWT decoding error: %s", str(e)) + logger.error(f"JWT decoding error: {e}") raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials", @@ -137,7 +137,7 @@ async def get_current_user_by_jwt( user = get_user_by_id(db, user_id) if user is None or not user.is_active: - logger.info("User not found or inactive: %s", user_id) + logger.info("User not found or inactive.") raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found or is inactive.",