From 7e3d470845446acdfa9c18892316fb01a285b6a6 Mon Sep 17 00:00:00 2001 From: Christophe Bornet Date: Wed, 2 Oct 2024 18:55:51 +0200 Subject: [PATCH] ref: Add ruff rules for bugbear (B) (#3983) Add ruff rules for bugbear (B) --- src/backend/base/langflow/api/utils.py | 6 ++--- src/backend/base/langflow/api/v1/chat.py | 4 ++-- src/backend/base/langflow/api/v1/endpoints.py | 9 ++++--- src/backend/base/langflow/api/v1/files.py | 14 +++++------ src/backend/base/langflow/api/v1/folders.py | 16 ++++++------- src/backend/base/langflow/api/v1/monitor.py | 16 ++++++------- src/backend/base/langflow/api/v1/store.py | 12 +++++----- src/backend/base/langflow/api/v1/variable.py | 4 ++-- .../langflow/base/tools/component_tool.py | 4 ++-- .../base/langflow/base/tools/flow_tool.py | 8 +++---- .../components/astra_assistants/run.py | 3 +-- .../langflow/components/data/APIRequest.py | 9 ++++--- .../deactivated/ChatLiteLLMModel.py | 4 ++-- .../deactivated/ExtractKeyFromData.py | 4 ++-- .../components/deactivated/ShouldRunNext.py | 2 +- .../GoogleGenerativeAIEmbeddings.py | 2 +- .../HuggingFaceInferenceAPIEmbeddings.py | 4 ++-- .../components/embeddings/NVIDIAEmbeddings.py | 6 ++--- .../embeddings/VertexAIEmbeddings.py | 4 ++-- .../components/helpers/ParseJSONData.py | 2 +- .../langchain_utilities/FirecrawlCrawlApi.py | 4 ++-- .../langchain_utilities/FirecrawlScrapeApi.py | 4 ++-- .../langchain_utilities/SpiderTool.py | 2 +- .../components/memories/AstraDBChatMemory.py | 4 ++-- .../memories/CassandraChatMemory.py | 4 ++-- .../components/memories/ZepChatMemory.py | 4 ++-- .../components/models/AmazonBedrockModel.py | 4 ++-- .../components/models/AnthropicModel.py | 4 ++-- .../models/GoogleGenerativeAIModel.py | 4 ++-- .../langflow/components/models/NvidiaModel.py | 6 ++--- .../components/models/VertexAiModel.py | 4 ++-- .../components/prototypes/FlowTool.py | 5 ++-- .../components/prototypes/JSONCleaner.py | 8 +++---- .../components/prototypes/SQLExecutor.py | 2 +- .../components/retrievers/NvidiaRerank.py | 6 ++--- .../components/tools/GoogleSearchAPI.py | 4 ++-- .../components/tools/PythonREPLTool.py | 4 ++-- .../langflow/components/tools/SearXNGTool.py | 3 ++- .../components/vectorstores/AstraDB.py | 14 ++++++----- .../components/vectorstores/Cassandra.py | 6 ++--- .../components/vectorstores/CassandraGraph.py | 4 ++-- .../components/vectorstores/Chroma.py | 4 ++-- .../components/vectorstores/Clickhouse.py | 2 +- .../components/vectorstores/Couchbase.py | 2 +- .../langflow/components/vectorstores/HCD.py | 12 +++++----- .../components/vectorstores/Milvus.py | 4 ++-- .../vectorstores/MongoDBAtlasVector.py | 6 ++--- .../components/vectorstores/Vectara.py | 4 ++-- .../components/vectorstores/vectara_rag.py | 4 ++-- .../base/langflow/custom/attributes.py | 4 ++-- .../custom/code_parser/code_parser.py | 4 ++-- .../custom/custom_component/base_component.py | 8 +++---- .../custom/custom_component/component.py | 18 +++++++------- .../custom_component/custom_component.py | 14 +++++------ .../directory_reader/directory_reader.py | 2 +- src/backend/base/langflow/custom/utils.py | 3 +-- src/backend/base/langflow/graph/graph/base.py | 13 +++++----- .../base/langflow/graph/graph/state_model.py | 2 +- .../base/langflow/graph/graph/utils.py | 6 ++--- src/backend/base/langflow/helpers/flow.py | 4 ++-- .../base/langflow/initial_setup/setup.py | 4 ++-- src/backend/base/langflow/inputs/inputs.py | 8 +++++-- src/backend/base/langflow/interface/run.py | 4 +--- src/backend/base/langflow/load/utils.py | 6 ++--- src/backend/base/langflow/logging/logger.py | 2 +- src/backend/base/langflow/memory.py | 3 +-- src/backend/base/langflow/schema/data.py | 4 ++-- src/backend/base/langflow/schema/dotdict.py | 8 +++---- src/backend/base/langflow/schema/message.py | 4 ++-- src/backend/base/langflow/services/base.py | 2 +- .../services/database/models/flow/model.py | 5 ++-- .../langflow/services/database/service.py | 2 +- src/backend/base/langflow/services/factory.py | 4 ++-- src/backend/base/langflow/services/manager.py | 2 +- .../base/langflow/services/store/service.py | 24 +++++++++---------- .../base/langflow/services/tracing/base.py | 3 ++- .../langflow/services/tracing/langfuse.py | 5 ++-- .../langflow/services/tracing/langsmith.py | 3 ++- .../langflow/services/tracing/langwatch.py | 3 ++- .../langflow/services/variable/service.py | 5 ++-- .../base/langflow/template/template/base.py | 2 +- src/backend/base/langflow/utils/validate.py | 4 ++-- src/backend/base/langflow/utils/version.py | 10 ++++---- src/backend/base/pyproject.toml | 7 ++++++ src/backend/tests/conftest.py | 16 +++++++++++++ .../tests/unit/graph/graph/test_base.py | 8 +++++-- 86 files changed, 267 insertions(+), 230 deletions(-) diff --git a/src/backend/base/langflow/api/utils.py b/src/backend/base/langflow/api/utils.py index 7ee577149..2a282fe5e 100644 --- a/src/backend/base/langflow/api/utils.py +++ b/src/backend/base/langflow/api/utils.py @@ -1,10 +1,10 @@ from __future__ import annotations import uuid -import warnings from typing import TYPE_CHECKING, Any from fastapi import HTTPException +from loguru import logger from sqlalchemy import delete from sqlmodel import Session @@ -101,7 +101,7 @@ async def check_langflow_version(component: StoreComponentCreate): if langflow_version is None: raise HTTPException(status_code=500, detail="Unable to verify the latest version of Langflow") if langflow_version != component.last_tested_version: - warnings.warn( + logger.warning( f"Your version of Langflow ({component.last_tested_version}) is outdated. " f"Please update to the latest version ({langflow_version}) and try again." ) @@ -260,4 +260,4 @@ async def cascade_delete_flow(session: Session, flow: Flow): session.exec(delete(Flow).where(Flow.id == flow.id)) # type: ignore except Exception as e: msg = f"Unable to cascade delete flow: ${flow.id}" - raise RuntimeError(msg, e) + raise RuntimeError(msg, e) from e diff --git a/src/backend/base/langflow/api/v1/chat.py b/src/backend/base/langflow/api/v1/chat.py index 6b1e969e8..86d866ada 100644 --- a/src/backend/base/langflow/api/v1/chat.py +++ b/src/backend/base/langflow/api/v1/chat.py @@ -134,7 +134,7 @@ async def retrieve_vertices_order( ), ) if "stream or streaming set to True" in str(exc): - raise HTTPException(status_code=400, detail=str(exc)) + raise HTTPException(status_code=400, detail=str(exc)) from exc logger.error(f"Error checking build status: {exc}") logger.exception(exc) raise HTTPException(status_code=500, detail=str(exc)) from exc @@ -202,7 +202,7 @@ async def build_flow( ), ) if "stream or streaming set to True" in str(exc): - raise HTTPException(status_code=400, detail=str(exc)) + raise HTTPException(status_code=400, detail=str(exc)) from exc logger.error(f"Error checking build status: {exc}") logger.exception(exc) raise HTTPException(status_code=500, detail=str(exc)) from exc diff --git a/src/backend/base/langflow/api/v1/endpoints.py b/src/backend/base/langflow/api/v1/endpoints.py index aaf8a513f..70c8ce798 100644 --- a/src/backend/base/langflow/api/v1/endpoints.py +++ b/src/backend/base/langflow/api/v1/endpoints.py @@ -176,7 +176,7 @@ async def simple_run_flow_task( async def simplified_run_flow( background_tasks: BackgroundTasks, flow: Annotated[FlowRead | None, Depends(get_flow_by_id_or_endpoint_name)], - input_request: SimplifiedAPIRequest = SimplifiedAPIRequest(), + input_request: SimplifiedAPIRequest | None = None, stream: bool = False, api_key_user: UserRead = Depends(api_key_security), telemetry_service: TelemetryService = Depends(get_telemetry_service), @@ -244,6 +244,7 @@ async def simplified_run_flow( supporting a wide range of applications by allowing for dynamic input and output configuration along with performance optimizations through session management and caching. """ + input_request = input_request if input_request is not None else SimplifiedAPIRequest() if flow is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Flow not found") start_time = time.perf_counter() @@ -379,8 +380,8 @@ async def webhook_run_flow( async def experimental_run_flow( session: Annotated[Session, Depends(get_session)], flow_id: UUID, - inputs: list[InputValueRequest] | None = [InputValueRequest(components=[], input_value="")], - outputs: list[str] | None = [], + inputs: list[InputValueRequest] | None = None, + outputs: list[str] | None = None, tweaks: Annotated[Tweaks | None, Body(embed=True)] = None, # noqa: F821 stream: Annotated[bool, Body(embed=True)] = False, # noqa: F821 session_id: Annotated[None | str, Body(embed=True)] = None, # noqa: F821 @@ -438,6 +439,8 @@ async def experimental_run_flow( flow_id_str = str(flow_id) if outputs is None: outputs = [] + if inputs is None: + inputs = [InputValueRequest(components=[], input_value="")] artifacts = {} if session_id: diff --git a/src/backend/base/langflow/api/v1/files.py b/src/backend/base/langflow/api/v1/files.py index 26bc8a3fe..96ddec2d9 100644 --- a/src/backend/base/langflow/api/v1/files.py +++ b/src/backend/base/langflow/api/v1/files.py @@ -64,7 +64,7 @@ async def upload_file( await storage_service.save_file(flow_id=folder, file_name=full_file_name, data=file_content) return UploadFileResponse(flowId=flow_id_str, file_path=f"{folder}/{full_file_name}") except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=500, detail=str(e)) from e @router.get("/download/{flow_id}/{file_name}") @@ -89,7 +89,7 @@ async def download_file(file_name: str, flow_id: UUID, storage_service: StorageS } return StreamingResponse(BytesIO(file_content), media_type=content_type, headers=headers) except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=500, detail=str(e)) from e @router.get("/images/{flow_id}/{file_name}") @@ -111,7 +111,7 @@ async def download_image(file_name: str, flow_id: UUID, storage_service: Storage file_content = await storage_service.get_file(flow_id=flow_id_str, file_name=file_name) return StreamingResponse(BytesIO(file_content), media_type=content_type) except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=500, detail=str(e)) from e @router.get("/profile_pictures/{folder_name}/{file_name}") @@ -130,7 +130,7 @@ async def download_profile_picture( return StreamingResponse(BytesIO(file_content), media_type=content_type) except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=500, detail=str(e)) from e @router.get("/profile_pictures/list") @@ -151,7 +151,7 @@ async def list_profile_pictures(storage_service: StorageService = Depends(get_st return {"files": files} except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=500, detail=str(e)) from e @router.get("/list/{flow_id}") @@ -163,7 +163,7 @@ async def list_files( files = await storage_service.list_files(flow_id=flow_id_str) return {"files": files} except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=500, detail=str(e)) from e @router.delete("/delete/{flow_id}/{file_name}") @@ -175,4 +175,4 @@ async def delete_file( await storage_service.delete_file(flow_id=flow_id_str, file_name=file_name) return {"message": f"File {file_name} deleted successfully"} except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=500, detail=str(e)) from e diff --git a/src/backend/base/langflow/api/v1/folders.py b/src/backend/base/langflow/api/v1/folders.py index c00c4ec5c..d3af3b1b6 100644 --- a/src/backend/base/langflow/api/v1/folders.py +++ b/src/backend/base/langflow/api/v1/folders.py @@ -74,7 +74,7 @@ def create_folder( return new_folder except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=500, detail=str(e)) from e @router.get("/", response_model=list[FolderRead], status_code=200) @@ -91,7 +91,7 @@ def read_folders( ).all() return sorted(folders, key=lambda x: x.name != DEFAULT_FOLDER_NAME) except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=500, detail=str(e)) from e @router.get("/{folder_id}", response_model=FolderReadWithFlows, status_code=200) @@ -110,8 +110,8 @@ def read_folder( return folder except Exception as e: if "No result found" in str(e): - raise HTTPException(status_code=404, detail="Folder not found") - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=404, detail="Folder not found") from e + raise HTTPException(status_code=500, detail=str(e)) from e @router.patch("/{folder_id}", response_model=FolderRead, status_code=200) @@ -167,7 +167,7 @@ def update_folder( return existing_folder except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=500, detail=str(e)) from e @router.delete("/{folder_id}", status_code=204) @@ -191,7 +191,7 @@ async def delete_folder( return Response(status_code=status.HTTP_204_NO_CONTENT) except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=500, detail=str(e)) from e @router.get("/download/{folder_id}", response_model=FlowListReadWithFolderName, status_code=200) @@ -206,8 +206,8 @@ async def download_file( return session.exec(select(Folder).where(Folder.id == folder_id, Folder.user_id == current_user.id)).first() except Exception as e: if "No result found" in str(e): - raise HTTPException(status_code=404, detail="Folder not found") - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=404, detail="Folder not found") from e + raise HTTPException(status_code=500, detail=str(e)) from e @router.post("/upload/", response_model=list[FlowRead], status_code=201) diff --git a/src/backend/base/langflow/api/v1/monitor.py b/src/backend/base/langflow/api/v1/monitor.py index c5b4e48f4..de374d573 100644 --- a/src/backend/base/langflow/api/v1/monitor.py +++ b/src/backend/base/langflow/api/v1/monitor.py @@ -29,7 +29,7 @@ async def get_vertex_builds( vertex_builds = get_vertex_builds_by_flow_id(session, flow_id) return VertexBuildMapModel.from_list_of_dicts(vertex_builds) except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=500, detail=str(e)) from e @router.delete("/builds", status_code=204) @@ -40,7 +40,7 @@ async def delete_vertex_builds( try: delete_vertex_builds_by_flow_id(session, flow_id) except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=500, detail=str(e)) from e @router.get("/messages", response_model=list[MessageResponse]) @@ -68,7 +68,7 @@ async def get_messages( messages = session.exec(stmt) return [MessageResponse.model_validate(d, from_attributes=True) for d in messages] except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=500, detail=str(e)) from e @router.delete("/messages", status_code=204) @@ -81,7 +81,7 @@ async def delete_messages( session.exec(delete(MessageTable).where(MessageTable.id.in_(message_ids))) # type: ignore session.commit() except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=500, detail=str(e)) from e @router.put("/messages/{message_id}", response_model=MessageRead) @@ -104,7 +104,7 @@ async def update_message( except HTTPException as e: raise e except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=500, detail=str(e)) from e @router.patch("/messages/session/{old_session_id}", response_model=list[MessageResponse]) @@ -137,7 +137,7 @@ async def update_session_id( except HTTPException as e: raise e except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=500, detail=str(e)) from e @router.delete("/messages/session/{session_id}", status_code=204) @@ -154,7 +154,7 @@ async def delete_messages_session( session.commit() return {"message": "Messages deleted successfully"} except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=500, detail=str(e)) from e @router.get("/transactions", response_model=list[TransactionReadResponse]) @@ -179,4 +179,4 @@ async def get_transactions( for t in transactions ] except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=500, detail=str(e)) from e diff --git a/src/backend/base/langflow/api/v1/store.py b/src/backend/base/langflow/api/v1/store.py index ffa7426d7..706518f50 100644 --- a/src/backend/base/langflow/api/v1/store.py +++ b/src/backend/base/langflow/api/v1/store.py @@ -67,7 +67,7 @@ async def check_if_store_has_api_key( try: is_valid = await store_service.check_api_key(api_key) except Exception as e: - raise HTTPException(status_code=400, detail=str(e)) + raise HTTPException(status_code=400, detail=str(e)) from e return {"has_api_key": api_key is not None, "is_valid": is_valid} @@ -82,7 +82,7 @@ async def share_component( await check_langflow_version(component) return await store_service.upload(store_api_key, component) except Exception as exc: - raise HTTPException(status_code=400, detail=str(exc)) + raise HTTPException(status_code=400, detail=str(exc)) from exc @router.patch("/components/{component_id}", response_model=CreateComponentResponse, status_code=201) @@ -96,7 +96,7 @@ async def update_shared_component( await check_langflow_version(component) return await store_service.update(store_api_key, component_id, component) except Exception as exc: - raise HTTPException(status_code=400, detail=str(exc)) + raise HTTPException(status_code=400, detail=str(exc)) from exc @router.get("/components/", response_model=ListComponentResponseModel) @@ -164,7 +164,7 @@ async def get_tags( except CustomException as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc except Exception as exc: - raise HTTPException(status_code=500, detail=str(exc)) + raise HTTPException(status_code=500, detail=str(exc)) from exc @router.get("/users/likes", response_model=list[UsersLikesResponse]) @@ -177,7 +177,7 @@ async def get_list_of_components_liked_by_user( except CustomException as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc except Exception as exc: - raise HTTPException(status_code=500, detail=str(exc)) + raise HTTPException(status_code=500, detail=str(exc)) from exc @router.post("/users/likes/{component_id}", response_model=UsersLikesResponse) @@ -194,4 +194,4 @@ async def like_component( except CustomException as exc: raise HTTPException(status_code=exc.status_code, detail=str(exc)) from exc except Exception as exc: - raise HTTPException(status_code=500, detail=str(exc)) + raise HTTPException(status_code=500, detail=str(exc)) from exc diff --git a/src/backend/base/langflow/api/v1/variable.py b/src/backend/base/langflow/api/v1/variable.py index e27392eb4..6d4dae238 100644 --- a/src/backend/base/langflow/api/v1/variable.py +++ b/src/backend/base/langflow/api/v1/variable.py @@ -83,8 +83,8 @@ def update_variable( variable=variable, session=session, ) - except NoResultFound: - raise HTTPException(status_code=404, detail="Variable not found") + except NoResultFound as e: + raise HTTPException(status_code=404, detail="Variable not found") from e except Exception as e: raise HTTPException(status_code=500, detail=str(e)) from e diff --git a/src/backend/base/langflow/base/tools/component_tool.py b/src/backend/base/langflow/base/tools/component_tool.py index e0fdbc369..d80fd33bb 100644 --- a/src/backend/base/langflow/base/tools/component_tool.py +++ b/src/backend/base/langflow/base/tools/component_tool.py @@ -1,12 +1,12 @@ from __future__ import annotations import re -import warnings from collections.abc import Callable from typing import TYPE_CHECKING from langchain_core.tools import BaseTool from langchain_core.tools.structured import StructuredTool +from loguru import logger from langflow.base.tools.constants import TOOL_OUTPUT_NAME from langflow.io.schema import create_input_schema @@ -27,7 +27,7 @@ def _get_input_type(input: InputTypes): def build_description(component: Component, output: Output): if not output.required_inputs: - warnings.warn(f"Output {output.name} does not have required inputs defined") + logger.warning(f"Output {output.name} does not have required inputs defined") if output.required_inputs: args = ", ".join( diff --git a/src/backend/base/langflow/base/tools/flow_tool.py b/src/backend/base/langflow/base/tools/flow_tool.py index d2bd68b78..8d5703fc9 100644 --- a/src/backend/base/langflow/base/tools/flow_tool.py +++ b/src/backend/base/langflow/base/tools/flow_tool.py @@ -1,10 +1,10 @@ from __future__ import annotations -import warnings from typing import Any from langchain_core.runnables import RunnableConfig from langchain_core.tools import BaseTool, ToolException +from loguru import logger from pydantic.v1 import BaseModel from langflow.base.flow_processing.utils import build_data_from_result_data, format_flow_output_data @@ -45,7 +45,7 @@ class FlowTool(BaseTool): """Use the tool.""" args_names = get_arg_names(self.inputs) if len(args_names) == len(args): - kwargs = {arg["arg_name"]: arg_value for arg, arg_value in zip(args_names, args)} + kwargs = {arg["arg_name"]: arg_value for arg, arg_value in zip(args_names, args, strict=True)} elif len(args_names) != len(args) and len(args) != 0: msg = "Number of arguments does not match the number of inputs. Pass keyword arguments instead." raise ToolException(msg) @@ -77,7 +77,7 @@ class FlowTool(BaseTool): raise ToolException(msg) if len(args) == len(args_names): - kwargs = {arg_name["arg_name"]: arg_value for arg_name, arg_value in zip(args_names, args)} + kwargs = {arg_name["arg_name"]: arg_value for arg_name, arg_value in zip(args_names, args, strict=True)} missing_args = [arg["arg_name"] for arg in args_names if arg["arg_name"] not in kwargs] if missing_args: @@ -101,7 +101,7 @@ class FlowTool(BaseTool): try: run_id = self.graph.run_id if self.graph else None except Exception as e: - warnings.warn(f"Failed to set run_id: {e}") + logger.warning(f"Failed to set run_id: {e}") run_id = None run_outputs = await run_flow( tweaks={key: {"input_value": value} for key, value in tweaks.items()}, diff --git a/src/backend/base/langflow/components/astra_assistants/run.py b/src/backend/base/langflow/components/astra_assistants/run.py index ebb184568..e7372b2d8 100644 --- a/src/backend/base/langflow/components/astra_assistants/run.py +++ b/src/backend/base/langflow/components/astra_assistants/run.py @@ -26,7 +26,6 @@ class AssistantsRun(Component): if field_value is None: thread = self.client.beta.threads.create() self.thread_id = thread.id - field_value build_config["thread_id"] = field_value inputs = [ @@ -92,4 +91,4 @@ class AssistantsRun(Component): except Exception as e: print(e) msg = f"Error running assistant: {e}" - raise Exception(msg) + raise Exception(msg) from e diff --git a/src/backend/base/langflow/components/data/APIRequest.py b/src/backend/base/langflow/components/data/APIRequest.py index c498d9baa..ce7880df1 100644 --- a/src/backend/base/langflow/components/data/APIRequest.py +++ b/src/backend/base/langflow/components/data/APIRequest.py @@ -94,7 +94,7 @@ class APIRequestComponent(Component): except Exception as exc: logger.error(f"Error parsing curl: {exc}") msg = f"Error parsing curl: {exc}" - raise ValueError(msg) + raise ValueError(msg) from exc return build_config def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None): @@ -123,7 +123,7 @@ class APIRequestComponent(Component): logger.error(f"Error decoding JSON data: {e}") body = None msg = f"Error decoding JSON data: {e}" - raise ValueError(msg) + raise ValueError(msg) from e data = body if body else None @@ -191,7 +191,10 @@ class APIRequestComponent(Component): async with httpx.AsyncClient() as client: results = await asyncio.gather( - *[self.make_request(client, method, u, headers, rec, timeout) for u, rec in zip(urls, bodies)] + *[ + self.make_request(client, method, u, headers, rec, timeout) + for u, rec in zip(urls, bodies, strict=True) + ] ) self.status = results return results diff --git a/src/backend/base/langflow/components/deactivated/ChatLiteLLMModel.py b/src/backend/base/langflow/components/deactivated/ChatLiteLLMModel.py index 5199e6961..0af61d39e 100644 --- a/src/backend/base/langflow/components/deactivated/ChatLiteLLMModel.py +++ b/src/backend/base/langflow/components/deactivated/ChatLiteLLMModel.py @@ -124,9 +124,9 @@ class ChatLiteLLMModelComponent(LCModelComponent): litellm.drop_params = True litellm.set_verbose = self.verbose - except ImportError: + except ImportError as e: msg = "Could not import litellm python package. " "Please install it with `pip install litellm`" - raise ChatLiteLLMException(msg) + raise ChatLiteLLMException(msg) from e # Remove empty keys if "" in self.kwargs: del self.kwargs[""] diff --git a/src/backend/base/langflow/components/deactivated/ExtractKeyFromData.py b/src/backend/base/langflow/components/deactivated/ExtractKeyFromData.py index 0dbb80c42..fcd622e23 100644 --- a/src/backend/base/langflow/components/deactivated/ExtractKeyFromData.py +++ b/src/backend/base/langflow/components/deactivated/ExtractKeyFromData.py @@ -38,10 +38,10 @@ class ExtractKeyFromDataComponent(CustomComponent): for key in keys: try: extracted_keys[key] = getattr(data, key) - except AttributeError: + except AttributeError as e: if not silent_error: msg = f"The key '{key}' does not exist in the data." - raise KeyError(msg) + raise KeyError(msg) from e return_data = Data(data=extracted_keys) self.status = return_data return return_data diff --git a/src/backend/base/langflow/components/deactivated/ShouldRunNext.py b/src/backend/base/langflow/components/deactivated/ShouldRunNext.py index 9369723c3..183f66a73 100644 --- a/src/backend/base/langflow/components/deactivated/ShouldRunNext.py +++ b/src/backend/base/langflow/components/deactivated/ShouldRunNext.py @@ -22,7 +22,7 @@ class ShouldRunNextComponent(CustomComponent): prompt = PromptTemplate.from_template(template) chain = prompt | llm error_message = "" - for i in range(retries): + for _i in range(retries): result = chain.invoke( {"question": question, "context": context, "error_message": error_message}, config={"callbacks": self.get_langchain_callbacks()}, diff --git a/src/backend/base/langflow/components/embeddings/GoogleGenerativeAIEmbeddings.py b/src/backend/base/langflow/components/embeddings/GoogleGenerativeAIEmbeddings.py index 7236e0433..1d3615851 100644 --- a/src/backend/base/langflow/components/embeddings/GoogleGenerativeAIEmbeddings.py +++ b/src/backend/base/langflow/components/embeddings/GoogleGenerativeAIEmbeddings.py @@ -78,7 +78,7 @@ class GoogleGenerativeAIEmbeddingsComponent(Component): title=title, output_dimensionality=1536, ) - for text, title in zip(batch, titles_batch) + for text, title in zip(batch, titles_batch, strict=True) ] try: diff --git a/src/backend/base/langflow/components/embeddings/HuggingFaceInferenceAPIEmbeddings.py b/src/backend/base/langflow/components/embeddings/HuggingFaceInferenceAPIEmbeddings.py index 05419e4d5..da871ccf2 100644 --- a/src/backend/base/langflow/components/embeddings/HuggingFaceInferenceAPIEmbeddings.py +++ b/src/backend/base/langflow/components/embeddings/HuggingFaceInferenceAPIEmbeddings.py @@ -55,12 +55,12 @@ class HuggingFaceInferenceAPIEmbeddingsComponent(LCEmbeddingsModel): try: response = requests.get(f"{inference_endpoint}/health", timeout=5) - except requests.RequestException: + except requests.RequestException as e: msg = ( f"Inference endpoint '{inference_endpoint}' is not responding. " "Please ensure the URL is correct and the service is running." ) - raise ValueError(msg) + raise ValueError(msg) from e if response.status_code != 200: msg = f"HuggingFace health check failed: {response.status_code}" diff --git a/src/backend/base/langflow/components/embeddings/NVIDIAEmbeddings.py b/src/backend/base/langflow/components/embeddings/NVIDIAEmbeddings.py index 4090692e8..10907805b 100644 --- a/src/backend/base/langflow/components/embeddings/NVIDIAEmbeddings.py +++ b/src/backend/base/langflow/components/embeddings/NVIDIAEmbeddings.py @@ -52,15 +52,15 @@ class NVIDIAEmbeddingsComponent(LCEmbeddingsModel): build_config["model"]["value"] = ids[0] except Exception as e: msg = f"Error getting model names: {e}" - raise ValueError(msg) + raise ValueError(msg) from e return build_config def build_embeddings(self) -> Embeddings: try: from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings - except ImportError: + except ImportError as e: msg = "Please install langchain-nvidia-ai-endpoints to use the Nvidia model." - raise ImportError(msg) + raise ImportError(msg) from e try: output = NVIDIAEmbeddings( model=self.model, diff --git a/src/backend/base/langflow/components/embeddings/VertexAIEmbeddings.py b/src/backend/base/langflow/components/embeddings/VertexAIEmbeddings.py index a0754e938..6c74f2651 100644 --- a/src/backend/base/langflow/components/embeddings/VertexAIEmbeddings.py +++ b/src/backend/base/langflow/components/embeddings/VertexAIEmbeddings.py @@ -38,9 +38,9 @@ class VertexAIEmbeddingsComponent(LCModelComponent): def build_embeddings(self) -> Embeddings: try: from langchain_google_vertexai import VertexAIEmbeddings - except ImportError: + except ImportError as e: msg = "Please install the langchain-google-vertexai package to use the VertexAIEmbeddings component." - raise ImportError(msg) + raise ImportError(msg) from e from google.oauth2 import service_account diff --git a/src/backend/base/langflow/components/helpers/ParseJSONData.py b/src/backend/base/langflow/components/helpers/ParseJSONData.py index ecb97e094..010d63da5 100644 --- a/src/backend/base/langflow/components/helpers/ParseJSONData.py +++ b/src/backend/base/langflow/components/helpers/ParseJSONData.py @@ -63,7 +63,7 @@ class ParseJSONDataComponent(Component): to_filter_as_dict.append(json.loads(repair_json(f))) except JSONDecodeError as e: msg = f"Invalid JSON: {e}" - raise ValueError(msg) + raise ValueError(msg) from e full_filter_str = json.dumps(to_filter_as_dict) diff --git a/src/backend/base/langflow/components/langchain_utilities/FirecrawlCrawlApi.py b/src/backend/base/langflow/components/langchain_utilities/FirecrawlCrawlApi.py index 7008b19c9..f9edeb5ac 100644 --- a/src/backend/base/langflow/components/langchain_utilities/FirecrawlCrawlApi.py +++ b/src/backend/base/langflow/components/langchain_utilities/FirecrawlCrawlApi.py @@ -56,11 +56,11 @@ class FirecrawlCrawlApi(CustomComponent): ) -> Data: try: from firecrawl.firecrawl import FirecrawlApp # type: ignore - except ImportError: + except ImportError as e: msg = ( "Could not import firecrawl integration package. " "Please install it with `pip install firecrawl-py`." ) - raise ImportError(msg) + raise ImportError(msg) from e crawler_options_dict = crawlerOptions.__dict__["data"]["text"] if crawlerOptions else {} page_options_dict = pageOptions.__dict__["data"]["text"] if pageOptions else {} diff --git a/src/backend/base/langflow/components/langchain_utilities/FirecrawlScrapeApi.py b/src/backend/base/langflow/components/langchain_utilities/FirecrawlScrapeApi.py index e4045cb32..81c3145df 100644 --- a/src/backend/base/langflow/components/langchain_utilities/FirecrawlScrapeApi.py +++ b/src/backend/base/langflow/components/langchain_utilities/FirecrawlScrapeApi.py @@ -49,11 +49,11 @@ class FirecrawlScrapeApi(CustomComponent): ) -> Data: try: from firecrawl.firecrawl import FirecrawlApp # type: ignore - except ImportError: + except ImportError as e: msg = ( "Could not import firecrawl integration package. " "Please install it with `pip install firecrawl-py`." ) - raise ImportError(msg) + raise ImportError(msg) from e extractor_options_dict = extractorOptions.__dict__["data"]["text"] if extractorOptions else {} page_options_dict = pageOptions.__dict__["data"]["text"] if pageOptions else {} diff --git a/src/backend/base/langflow/components/langchain_utilities/SpiderTool.py b/src/backend/base/langflow/components/langchain_utilities/SpiderTool.py index a0fd38fb2..7b7800414 100644 --- a/src/backend/base/langflow/components/langchain_utilities/SpiderTool.py +++ b/src/backend/base/langflow/components/langchain_utilities/SpiderTool.py @@ -114,7 +114,7 @@ class SpiderTool(Component): raise ValueError(msg) except Exception as e: msg = f"Error: {str(e)}" - raise Exception(msg) + raise Exception(msg) from e records = [] diff --git a/src/backend/base/langflow/components/memories/AstraDBChatMemory.py b/src/backend/base/langflow/components/memories/AstraDBChatMemory.py index ae524c6aa..be53f03e9 100644 --- a/src/backend/base/langflow/components/memories/AstraDBChatMemory.py +++ b/src/backend/base/langflow/components/memories/AstraDBChatMemory.py @@ -52,12 +52,12 @@ class AstraDBChatMemory(LCChatMemoryComponent): def build_message_history(self) -> BaseChatMessageHistory: try: from langchain_astradb.chat_message_histories import AstraDBChatMessageHistory - except ImportError: + except ImportError as e: msg = ( "Could not import langchain Astra DB integration package. " "Please install it with `pip install langchain-astradb`." ) - raise ImportError(msg) + raise ImportError(msg) from e return AstraDBChatMessageHistory( session_id=self.session_id, diff --git a/src/backend/base/langflow/components/memories/CassandraChatMemory.py b/src/backend/base/langflow/components/memories/CassandraChatMemory.py index e87d83b94..69812c00e 100644 --- a/src/backend/base/langflow/components/memories/CassandraChatMemory.py +++ b/src/backend/base/langflow/components/memories/CassandraChatMemory.py @@ -54,9 +54,9 @@ class CassandraChatMemory(LCChatMemoryComponent): try: import cassio - except ImportError: + except ImportError as e: msg = "Could not import cassio integration package. " "Please install it with `pip install cassio`." - raise ImportError(msg) + raise ImportError(msg) from e from uuid import UUID diff --git a/src/backend/base/langflow/components/memories/ZepChatMemory.py b/src/backend/base/langflow/components/memories/ZepChatMemory.py index 1914b6e2b..2edb9a6c0 100644 --- a/src/backend/base/langflow/components/memories/ZepChatMemory.py +++ b/src/backend/base/langflow/components/memories/ZepChatMemory.py @@ -34,9 +34,9 @@ class ZepChatMemory(LCChatMemoryComponent): from zep_python.langchain import ZepChatMessageHistory zep_python.zep_client.API_BASE_PATH = self.api_base_path - except ImportError: + except ImportError as e: msg = "Could not import zep-python package. " "Please install it with `pip install zep-python`." - raise ImportError(msg) + raise ImportError(msg) from e zep_client = ZepClient(api_url=self.url, api_key=self.api_key) return ZepChatMessageHistory(session_id=self.session_id, zep_client=zep_client) diff --git a/src/backend/base/langflow/components/models/AmazonBedrockModel.py b/src/backend/base/langflow/components/models/AmazonBedrockModel.py index 4d4f128d8..92ebd5a86 100644 --- a/src/backend/base/langflow/components/models/AmazonBedrockModel.py +++ b/src/backend/base/langflow/components/models/AmazonBedrockModel.py @@ -68,9 +68,9 @@ class AmazonBedrockComponent(LCModelComponent): def build_model(self) -> LanguageModel: # type: ignore[type-var] try: from langchain_aws import ChatBedrock - except ImportError: + except ImportError as e: msg = "langchain_aws is not installed. Please install it with `pip install langchain_aws`." - raise ImportError(msg) + raise ImportError(msg) from e if self.aws_access_key: import boto3 # type: ignore diff --git a/src/backend/base/langflow/components/models/AnthropicModel.py b/src/backend/base/langflow/components/models/AnthropicModel.py index c7a6cbf85..c846c3d2e 100644 --- a/src/backend/base/langflow/components/models/AnthropicModel.py +++ b/src/backend/base/langflow/components/models/AnthropicModel.py @@ -62,9 +62,9 @@ class AnthropicModelComponent(LCModelComponent): def build_model(self) -> LanguageModel: # type: ignore[type-var] try: from langchain_anthropic.chat_models import ChatAnthropic - except ImportError: + except ImportError as e: msg = "langchain_anthropic is not installed. Please install it with `pip install langchain_anthropic`." - raise ImportError(msg) + raise ImportError(msg) from e model = self.model anthropic_api_key = self.anthropic_api_key max_tokens = self.max_tokens diff --git a/src/backend/base/langflow/components/models/GoogleGenerativeAIModel.py b/src/backend/base/langflow/components/models/GoogleGenerativeAIModel.py index be5357b10..bca14f4e9 100644 --- a/src/backend/base/langflow/components/models/GoogleGenerativeAIModel.py +++ b/src/backend/base/langflow/components/models/GoogleGenerativeAIModel.py @@ -62,9 +62,9 @@ class GoogleGenerativeAIComponent(LCModelComponent): def build_model(self) -> LanguageModel: # type: ignore[type-var] try: from langchain_google_genai import ChatGoogleGenerativeAI - except ImportError: + except ImportError as e: msg = "The 'langchain_google_genai' package is required to use the Google Generative AI model." - raise ImportError(msg) + raise ImportError(msg) from e google_api_key = self.google_api_key model = self.model diff --git a/src/backend/base/langflow/components/models/NvidiaModel.py b/src/backend/base/langflow/components/models/NvidiaModel.py index c2c58d7ec..89b568aa2 100644 --- a/src/backend/base/langflow/components/models/NvidiaModel.py +++ b/src/backend/base/langflow/components/models/NvidiaModel.py @@ -66,15 +66,15 @@ class NVIDIAModelComponent(LCModelComponent): build_config["model_name"]["value"] = ids[0] except Exception as e: msg = f"Error getting model names: {e}" - raise ValueError(msg) + raise ValueError(msg) from e return build_config def build_model(self) -> LanguageModel: # type: ignore[type-var] try: from langchain_nvidia_ai_endpoints import ChatNVIDIA - except ImportError: + except ImportError as e: msg = "Please install langchain-nvidia-ai-endpoints to use the NVIDIA model." - raise ImportError(msg) + raise ImportError(msg) from e nvidia_api_key = self.nvidia_api_key temperature = self.temperature model_name: str = self.model_name diff --git a/src/backend/base/langflow/components/models/VertexAiModel.py b/src/backend/base/langflow/components/models/VertexAiModel.py index 7fff6ee70..2c7beee2b 100644 --- a/src/backend/base/langflow/components/models/VertexAiModel.py +++ b/src/backend/base/langflow/components/models/VertexAiModel.py @@ -41,9 +41,9 @@ class ChatVertexAIComponent(LCModelComponent): def build_model(self) -> LanguageModel: try: from langchain_google_vertexai import ChatVertexAI - except ImportError: + except ImportError as e: msg = "Please install the langchain-google-vertexai package to use the VertexAIEmbeddings component." - raise ImportError(msg) + raise ImportError(msg) from e location = self.location or None if self.credentials: from google.cloud import aiplatform diff --git a/src/backend/base/langflow/components/prototypes/FlowTool.py b/src/backend/base/langflow/components/prototypes/FlowTool.py index 30b950965..0c6474c7b 100644 --- a/src/backend/base/langflow/components/prototypes/FlowTool.py +++ b/src/backend/base/langflow/components/prototypes/FlowTool.py @@ -1,6 +1,7 @@ -import warnings from typing import Any +from loguru import logger + from langflow.base.langchain_utilities.model import LCToolComponent from langflow.base.tools.flow_tool import FlowTool from langflow.field_typing import Tool @@ -85,7 +86,7 @@ class FlowToolComponent(LCToolComponent): try: graph.set_run_id(self.graph.run_id) except Exception as e: - warnings.warn(f"Failed to set run_id: {e}") + logger.warning(f"Failed to set run_id: {e}") inputs = get_flow_inputs(graph) tool = FlowTool( name=self.name, diff --git a/src/backend/base/langflow/components/prototypes/JSONCleaner.py b/src/backend/base/langflow/components/prototypes/JSONCleaner.py index f51379177..3e4fd49e1 100644 --- a/src/backend/base/langflow/components/prototypes/JSONCleaner.py +++ b/src/backend/base/langflow/components/prototypes/JSONCleaner.py @@ -47,9 +47,9 @@ class JSONCleaner(Component): def clean_json(self) -> Message: try: from json_repair import repair_json # type: ignore - except ImportError: + except ImportError as e: msg = "Could not import the json_repair package." "Please install it with `pip install json_repair`." - raise ImportError(msg) + raise ImportError(msg) from e """Clean the input JSON string based on provided options and return the cleaned JSON string.""" json_str = self.json_str @@ -79,7 +79,7 @@ class JSONCleaner(Component): return Message(text=result) except Exception as e: msg = f"Error cleaning JSON string: {str(e)}" - raise ValueError(msg) + raise ValueError(msg) from e def _remove_control_characters(self, s: str) -> str: """Remove control characters from the string.""" @@ -96,4 +96,4 @@ class JSONCleaner(Component): return s except json.JSONDecodeError as e: msg = f"Invalid JSON string: {str(e)}" - raise ValueError(msg) + raise ValueError(msg) from e diff --git a/src/backend/base/langflow/components/prototypes/SQLExecutor.py b/src/backend/base/langflow/components/prototypes/SQLExecutor.py index 4c86fe276..d964c1a0f 100644 --- a/src/backend/base/langflow/components/prototypes/SQLExecutor.py +++ b/src/backend/base/langflow/components/prototypes/SQLExecutor.py @@ -49,7 +49,7 @@ class SQLExecutorComponent(CustomComponent): database = SQLDatabase.from_uri(database_url) except Exception as e: msg = f"An error occurred while connecting to the database: {e}" - raise ValueError(msg) + raise ValueError(msg) from e try: tool = QuerySQLDataBaseTool(db=database) result = tool.run(query, include_columns=include_columns) diff --git a/src/backend/base/langflow/components/retrievers/NvidiaRerank.py b/src/backend/base/langflow/components/retrievers/NvidiaRerank.py index e699a036b..70a47d6b4 100644 --- a/src/backend/base/langflow/components/retrievers/NvidiaRerank.py +++ b/src/backend/base/langflow/components/retrievers/NvidiaRerank.py @@ -56,15 +56,15 @@ class NvidiaRerankComponent(LCVectorStoreComponent): build_config["model"]["value"] = ids[0] except Exception as e: msg = f"Error getting model names: {e}" - raise ValueError(msg) + raise ValueError(msg) from e return build_config def build_model(self): try: from langchain_nvidia_ai_endpoints import NVIDIARerank - except ImportError: + except ImportError as e: msg = "Please install langchain-nvidia-ai-endpoints to use the NVIDIA model." - raise ImportError(msg) + raise ImportError(msg) from e return NVIDIARerank(api_key=self.api_key, model=self.model, base_url=self.base_url) def build_base_retriever(self) -> Retriever: # type: ignore[type-var] diff --git a/src/backend/base/langflow/components/tools/GoogleSearchAPI.py b/src/backend/base/langflow/components/tools/GoogleSearchAPI.py index 0d6ca83d6..c1f1ef57d 100644 --- a/src/backend/base/langflow/components/tools/GoogleSearchAPI.py +++ b/src/backend/base/langflow/components/tools/GoogleSearchAPI.py @@ -38,7 +38,7 @@ class GoogleSearchAPIComponent(LCToolComponent): def _build_wrapper(self): try: from langchain_google_community import GoogleSearchAPIWrapper # type: ignore - except ImportError: + except ImportError as e: msg = "Please install langchain-google-community to use GoogleSearchAPIWrapper." - raise ImportError(msg) + raise ImportError(msg) from e return GoogleSearchAPIWrapper(google_api_key=self.google_api_key, google_cse_id=self.google_cse_id, k=self.k) diff --git a/src/backend/base/langflow/components/tools/PythonREPLTool.py b/src/backend/base/langflow/components/tools/PythonREPLTool.py index 268cf4b03..1b4040b0a 100644 --- a/src/backend/base/langflow/components/tools/PythonREPLTool.py +++ b/src/backend/base/langflow/components/tools/PythonREPLTool.py @@ -61,9 +61,9 @@ class PythonREPLToolComponent(LCToolComponent): try: imported_module = importlib.import_module(module) global_dict[imported_module.__name__] = imported_module - except ImportError: + except ImportError as e: msg = f"Could not import module {module}" - raise ImportError(msg) + raise ImportError(msg) from e return global_dict def build_tool(self) -> Tool: diff --git a/src/backend/base/langflow/components/tools/SearXNGTool.py b/src/backend/base/langflow/components/tools/SearXNGTool.py index a55daaec5..8d7016ccc 100644 --- a/src/backend/base/langflow/components/tools/SearXNGTool.py +++ b/src/backend/base/langflow/components/tools/SearXNGTool.py @@ -1,4 +1,5 @@ import json +from collections.abc import Sequence from typing import Any import requests @@ -87,7 +88,7 @@ class SearXNGToolComponent(LCToolComponent): _max_results: int = 10 @staticmethod - def search(query: str, categories: list[str] = []) -> list: + def search(query: str, categories: Sequence[str] = ()) -> list: if not SearxSearch._categories and not categories: msg = "No categories provided." raise ValueError(msg) diff --git a/src/backend/base/langflow/components/vectorstores/AstraDB.py b/src/backend/base/langflow/components/vectorstores/AstraDB.py index 641d8e066..c7ebb77fa 100644 --- a/src/backend/base/langflow/components/vectorstores/AstraDB.py +++ b/src/backend/base/langflow/components/vectorstores/AstraDB.py @@ -213,11 +213,13 @@ class AstraVectorStoreComponent(LCVectorStoreComponent): items = list(build_config.items()) # Find the index of the key to insert after - for i, (key, value) in enumerate(items): + idx = len(items) + for i, (key, _value) in enumerate(items): if key == field_name: + idx = i + 1 break - items.insert(i + 1, (new_field_name, new_parameter)) + items.insert(idx, (new_field_name, new_parameter)) # Clear the original dictionary and update with the modified items build_config.clear() @@ -366,21 +368,21 @@ class AstraVectorStoreComponent(LCVectorStoreComponent): try: from langchain_astradb import AstraDBVectorStore from langchain_astradb.utils.astradb import SetupMode - except ImportError: + except ImportError as e: msg = ( "Could not import langchain Astra DB integration package. " "Please install it with `pip install langchain-astradb`." ) - raise ImportError(msg) + raise ImportError(msg) from e try: if not self.setup_mode: self.setup_mode = self._inputs["setup_mode"].options[0] setup_mode_value = SetupMode[self.setup_mode.upper()] - except KeyError: + except KeyError as e: msg = f"Invalid setup mode: {self.setup_mode}" - raise ValueError(msg) + raise ValueError(msg) from e if self.embedding: embedding_dict = {"embedding": self.embedding} diff --git a/src/backend/base/langflow/components/vectorstores/Cassandra.py b/src/backend/base/langflow/components/vectorstores/Cassandra.py index 1c729921b..e4293b9ec 100644 --- a/src/backend/base/langflow/components/vectorstores/Cassandra.py +++ b/src/backend/base/langflow/components/vectorstores/Cassandra.py @@ -136,9 +136,9 @@ class CassandraVectorStoreComponent(LCVectorStoreComponent): try: import cassio from langchain_community.utilities.cassandra import SetupMode - except ImportError: + except ImportError as e: msg = "Could not import cassio integration package. " "Please install it with `pip install cassio`." - raise ImportError(msg) + raise ImportError(msg) from e from uuid import UUID @@ -234,7 +234,7 @@ class CassandraVectorStoreComponent(LCVectorStoreComponent): "You should ingest data through Langflow (or LangChain) to query it in Langflow. " "Your collection does not contain a field name 'content'." ) - raise ValueError(msg) + raise ValueError(msg) from e raise e logger.debug(f"Retrieved documents: {len(docs)}") diff --git a/src/backend/base/langflow/components/vectorstores/CassandraGraph.py b/src/backend/base/langflow/components/vectorstores/CassandraGraph.py index 4336f6209..6d0ed872e 100644 --- a/src/backend/base/langflow/components/vectorstores/CassandraGraph.py +++ b/src/backend/base/langflow/components/vectorstores/CassandraGraph.py @@ -125,9 +125,9 @@ class CassandraGraphVectorStoreComponent(LCVectorStoreComponent): try: import cassio from langchain_community.utilities.cassandra import SetupMode - except ImportError: + except ImportError as e: msg = "Could not import cassio integration package. " "Please install it with `pip install cassio`." - raise ImportError(msg) + raise ImportError(msg) from e database_ref = self.database_ref diff --git a/src/backend/base/langflow/components/vectorstores/Chroma.py b/src/backend/base/langflow/components/vectorstores/Chroma.py index 4a6ffb291..affdfb0ea 100644 --- a/src/backend/base/langflow/components/vectorstores/Chroma.py +++ b/src/backend/base/langflow/components/vectorstores/Chroma.py @@ -106,11 +106,11 @@ class ChromaVectorStoreComponent(LCVectorStoreComponent): try: from chromadb import Client from langchain_chroma import Chroma - except ImportError: + except ImportError as e: msg = ( "Could not import Chroma integration package. " "Please install it with `pip install langchain-chroma`." ) - raise ImportError(msg) + raise ImportError(msg) from e # Chroma settings chroma_settings = None client = None diff --git a/src/backend/base/langflow/components/vectorstores/Clickhouse.py b/src/backend/base/langflow/components/vectorstores/Clickhouse.py index 73fe77240..faf501e3b 100644 --- a/src/backend/base/langflow/components/vectorstores/Clickhouse.py +++ b/src/backend/base/langflow/components/vectorstores/Clickhouse.py @@ -83,7 +83,7 @@ class ClickhouseVectorStoreComponent(LCVectorStoreComponent): client.command("SELECT 1") except Exception as e: msg = f"Failed to connect to Clickhouse: {e}" - raise ValueError(msg) + raise ValueError(msg) from e documents = [] for _input in self.ingest_data or []: diff --git a/src/backend/base/langflow/components/vectorstores/Couchbase.py b/src/backend/base/langflow/components/vectorstores/Couchbase.py index 6ef111bde..ec6806a9e 100644 --- a/src/backend/base/langflow/components/vectorstores/Couchbase.py +++ b/src/backend/base/langflow/components/vectorstores/Couchbase.py @@ -59,7 +59,7 @@ class CouchbaseVectorStoreComponent(LCVectorStoreComponent): cluster.wait_until_ready(timedelta(seconds=5)) except Exception as e: msg = f"Failed to connect to Couchbase: {e}" - raise ValueError(msg) + raise ValueError(msg) from e documents = [] for _input in self.ingest_data or []: diff --git a/src/backend/base/langflow/components/vectorstores/HCD.py b/src/backend/base/langflow/components/vectorstores/HCD.py index 9a9eb083e..3ead01b18 100644 --- a/src/backend/base/langflow/components/vectorstores/HCD.py +++ b/src/backend/base/langflow/components/vectorstores/HCD.py @@ -180,28 +180,28 @@ class HCDVectorStoreComponent(LCVectorStoreComponent): try: from langchain_astradb import AstraDBVectorStore from langchain_astradb.utils.astradb import SetupMode - except ImportError: + except ImportError as e: msg = ( "Could not import langchain Astra DB integration package. " "Please install it with `pip install langchain-astradb`." ) - raise ImportError(msg) + raise ImportError(msg) from e try: from astrapy.authentication import UsernamePasswordTokenProvider from astrapy.constants import Environment - except ImportError: + except ImportError as e: msg = "Could not import astrapy integration package. " "Please install it with `pip install astrapy`." - raise ImportError(msg) + raise ImportError(msg) from e try: if not self.setup_mode: self.setup_mode = self._inputs["setup_mode"].options[0] setup_mode_value = SetupMode[self.setup_mode.upper()] - except KeyError: + except KeyError as e: msg = f"Invalid setup mode: {self.setup_mode}" - raise ValueError(msg) + raise ValueError(msg) from e if not isinstance(self.embedding, dict): embedding_dict = {"embedding": self.embedding} diff --git a/src/backend/base/langflow/components/vectorstores/Milvus.py b/src/backend/base/langflow/components/vectorstores/Milvus.py index c55293fe3..f2ca43bb3 100644 --- a/src/backend/base/langflow/components/vectorstores/Milvus.py +++ b/src/backend/base/langflow/components/vectorstores/Milvus.py @@ -73,11 +73,11 @@ class MilvusVectorStoreComponent(LCVectorStoreComponent): def build_vector_store(self): try: from langchain_milvus.vectorstores import Milvus as LangchainMilvus - except ImportError: + except ImportError as e: msg = ( "Could not import Milvus integration package. " "Please install it with `pip install langchain-milvus`." ) - raise ImportError(msg) + raise ImportError(msg) from e self.connection_args.update(uri=self.uri, token=self.password) milvus_store = LangchainMilvus( embedding_function=self.embedding, diff --git a/src/backend/base/langflow/components/vectorstores/MongoDBAtlasVector.py b/src/backend/base/langflow/components/vectorstores/MongoDBAtlasVector.py index 31fb620f5..cd17e49fd 100644 --- a/src/backend/base/langflow/components/vectorstores/MongoDBAtlasVector.py +++ b/src/backend/base/langflow/components/vectorstores/MongoDBAtlasVector.py @@ -38,16 +38,16 @@ class MongoVectorStoreComponent(LCVectorStoreComponent): def build_vector_store(self) -> MongoDBAtlasVectorSearch: try: from pymongo import MongoClient - except ImportError: + except ImportError as e: msg = "Please install pymongo to use MongoDB Atlas Vector Store" - raise ImportError(msg) + raise ImportError(msg) from e try: mongo_client: MongoClient = MongoClient(self.mongodb_atlas_cluster_uri) collection = mongo_client[self.db_name][self.collection_name] except Exception as e: msg = f"Failed to connect to MongoDB Atlas: {e}" - raise ValueError(msg) + raise ValueError(msg) from e documents = [] for _input in self.ingest_data or []: diff --git a/src/backend/base/langflow/components/vectorstores/Vectara.py b/src/backend/base/langflow/components/vectorstores/Vectara.py index de256cecd..a43806b1e 100644 --- a/src/backend/base/langflow/components/vectorstores/Vectara.py +++ b/src/backend/base/langflow/components/vectorstores/Vectara.py @@ -58,9 +58,9 @@ class VectaraVectorStoreComponent(LCVectorStoreComponent): """ try: from langchain_community.vectorstores import Vectara - except ImportError: + except ImportError as e: msg = "Could not import Vectara. Please install it with `pip install langchain-community`." - raise ImportError(msg) + raise ImportError(msg) from e vectara = Vectara( vectara_customer_id=self.vectara_customer_id, diff --git a/src/backend/base/langflow/components/vectorstores/vectara_rag.py b/src/backend/base/langflow/components/vectorstores/vectara_rag.py index 47f54643e..3ec48e98b 100644 --- a/src/backend/base/langflow/components/vectorstores/vectara_rag.py +++ b/src/backend/base/langflow/components/vectorstores/vectara_rag.py @@ -136,9 +136,9 @@ class VectaraRagComponent(Component): try: from langchain_community.vectorstores import Vectara from langchain_community.vectorstores.vectara import RerankConfig, SummaryConfig, VectaraQueryConfig - except ImportError: + except ImportError as e: msg = "Could not import Vectara. Please install it with `pip install langchain-community`." - raise ImportError(msg) + raise ImportError(msg) from e vectara = Vectara(self.vectara_customer_id, self.vectara_corpus_id, self.vectara_api_key) rerank_config = RerankConfig(self.reranker, self.reranker_k, self.diversity_bias) diff --git a/src/backend/base/langflow/custom/attributes.py b/src/backend/base/langflow/custom/attributes.py index 9235f0966..a84d41e60 100644 --- a/src/backend/base/langflow/custom/attributes.py +++ b/src/backend/base/langflow/custom/attributes.py @@ -1,7 +1,7 @@ -import warnings from collections.abc import Callable import emoji +from loguru import logger def validate_icon(value: str, *args, **kwargs): @@ -18,7 +18,7 @@ def validate_icon(value: str, *args, **kwargs): emoji_value = emoji.emojize(value, variant="emoji_type") if value == emoji_value: - warnings.warn(f"Invalid emoji. {value} is not a valid emoji.") + logger.warning(f"Invalid emoji. {value} is not a valid emoji.") return value return emoji_value diff --git a/src/backend/base/langflow/custom/code_parser/code_parser.py b/src/backend/base/langflow/custom/code_parser/code_parser.py index 78f59959b..704cf39d4 100644 --- a/src/backend/base/langflow/custom/code_parser/code_parser.py +++ b/src/backend/base/langflow/custom/code_parser/code_parser.py @@ -217,7 +217,7 @@ class CodeParser: defaults = missing_defaults + default_values - return [self.parse_arg(arg, default) for arg, default in zip(node.args.args, defaults)] + return [self.parse_arg(arg, default) for arg, default in zip(node.args.args, defaults, strict=True)] def parse_varargs(self, node: ast.FunctionDef) -> list[dict[str, Any]]: """ @@ -238,7 +238,7 @@ class CodeParser: ast.unparse(default) if default else None for default in node.args.kw_defaults ] - return [self.parse_arg(arg, default) for arg, default in zip(node.args.kwonlyargs, kw_defaults)] + return [self.parse_arg(arg, default) for arg, default in zip(node.args.kwonlyargs, kw_defaults, strict=True)] def parse_kwargs(self, node: ast.FunctionDef) -> list[dict[str, Any]]: """ diff --git a/src/backend/base/langflow/custom/custom_component/base_component.py b/src/backend/base/langflow/custom/custom_component/base_component.py index 68cdca07b..366de38cf 100644 --- a/src/backend/base/langflow/custom/custom_component/base_component.py +++ b/src/backend/base/langflow/custom/custom_component/base_component.py @@ -1,10 +1,10 @@ import operator -import warnings from typing import Any, ClassVar from uuid import UUID from cachetools import TTLCache, cachedmethod from fastapi import HTTPException +from loguru import logger from langflow.custom.attributes import ATTR_FUNC_MAPPING from langflow.custom.code_parser import CodeParser @@ -35,13 +35,13 @@ class BaseComponent: self.cache = TTLCache(maxsize=1024, ttl=60) for key, value in data.items(): if key == "user_id": - setattr(self, "_user_id", value) + self._user_id = value else: setattr(self, key, value) def __setattr__(self, key, value): - if key == "_user_id" and hasattr(self, "_user_id") and getattr(self, "_user_id") is not None: - warnings.warn("user_id is immutable and cannot be changed.") + if key == "_user_id" and self._user_id is not None: + logger.warning("user_id is immutable and cannot be changed.") super().__setattr__(key, value) @cachedmethod(cache=operator.attrgetter("cache")) diff --git a/src/backend/base/langflow/custom/custom_component/component.py b/src/backend/base/langflow/custom/custom_component/component.py index 4c03a1f08..f06e96301 100644 --- a/src/backend/base/langflow/custom/custom_component/component.py +++ b/src/backend/base/langflow/custom/custom_component/component.py @@ -113,7 +113,7 @@ class Component(CustomComponent): def _reset_all_output_values(self): if isinstance(self._outputs_map, dict): for output in self._outputs_map.values(): - setattr(output, "value", UNDEFINED) + output.value = UNDEFINED def _build_state_model(self): if self._state_model: @@ -164,9 +164,9 @@ class Component(CustomComponent): raise ValueError(msg) class_code = inspect.getsource(module) self._code = class_code - except OSError: + except OSError as e: msg = f"Could not find source code for {self.__class__.__name__}" - raise ValueError(msg) + raise ValueError(msg) from e def set(self, **kwargs): """ @@ -441,9 +441,9 @@ class Component(CustomComponent): if callable(value) and self._inherits_from_component(value): try: self._method_is_valid_output(value) - except ValueError: + except ValueError as e: msg = f"Method {value.__name__} is not a valid output of {value.__self__.__class__.__name__}" - raise ValueError(msg) + raise ValueError(msg) from e self._connect_to_component(key, value, _input) else: self._set_parameter_or_attribute(key, value) @@ -569,15 +569,15 @@ class Component(CustomComponent): for name, value in self._parameters.items(): try: template[name]["value"] = value - except KeyError: + except KeyError as e: close_match = find_closest_match(name, list(template.keys())) if close_match: msg = ( f"Parameter '{name}' not found in {self.__class__.__name__}. " f"Did you mean '{close_match}'?" ) - raise ValueError(msg) + raise ValueError(msg) from e msg = f"Parameter {name} not found in {self.__class__.__name__}. " - raise ValueError(msg) + raise ValueError(msg) from e def _get_method_return_type(self, method_name: str) -> list[str]: method = getattr(self, method_name) @@ -594,7 +594,7 @@ class Component(CustomComponent): #! works and then update this later field_config = self.get_template_config(self) frontend_node = ComponentFrontendNode.from_inputs(**field_config) - for key, value in self._inputs.items(): + for key, _value in self._inputs.items(): frontend_node.set_field_load_from_db_in_template(key, False) self._map_parameters_on_frontend_node(frontend_node) diff --git a/src/backend/base/langflow/custom/custom_component/custom_component.py b/src/backend/base/langflow/custom/custom_component/custom_component.py index b7611997a..07a16b2da 100644 --- a/src/backend/base/langflow/custom/custom_component/custom_component.py +++ b/src/backend/base/langflow/custom/custom_component/custom_component.py @@ -118,7 +118,7 @@ class CustomComponent(BaseComponent): self._vertex.graph.update_state(name=name, record=value, caller=self._vertex.id) except Exception as e: msg = f"Error updating state: {e}" - raise ValueError(msg) + raise ValueError(msg) from e def stop(self, output_name: str | None = None): if not output_name and self._vertex and len(self._vertex.outputs) == 1: @@ -133,7 +133,7 @@ class CustomComponent(BaseComponent): self.graph.mark_branch(vertex_id=self._vertex.id, output_name=output_name, state="INACTIVE") except Exception as e: msg = f"Error stopping {self.display_name}: {e}" - raise ValueError(msg) + raise ValueError(msg) from e def append_state(self, name: str, value: Any): if not self._vertex: @@ -143,7 +143,7 @@ class CustomComponent(BaseComponent): self._vertex.graph.append_state(name=name, record=value, caller=self._vertex.id) except Exception as e: msg = f"Error appending state: {e}" - raise ValueError(msg) + raise ValueError(msg) from e def get_state(self, name: str): if not self._vertex: @@ -153,7 +153,7 @@ class CustomComponent(BaseComponent): return self._vertex.graph.get_state(name=name) except Exception as e: msg = f"Error getting state: {e}" - raise ValueError(msg) + raise ValueError(msg) from e @staticmethod def resolve_path(path: str) -> str: @@ -278,9 +278,9 @@ class CustomComponent(BaseComponent): else: try: data_dict[key] = model_dump[key] - except KeyError: + except KeyError as e: msg = f"Key {key} not found in {item}" - raise ValueError(msg) + raise ValueError(msg) from e elif isinstance(item, str): data_dict = {"text": item} @@ -512,7 +512,7 @@ class CustomComponent(BaseComponent): return list_flows(user_id=str(self._user_id)) except Exception as e: msg = f"Error listing flows: {e}" - raise ValueError(msg) + raise ValueError(msg) from e def build(self, *args: Any, **kwargs: Any) -> Any: """ diff --git a/src/backend/base/langflow/custom/directory_reader/directory_reader.py b/src/backend/base/langflow/custom/directory_reader/directory_reader.py index fe7bdb140..42092e3c1 100644 --- a/src/backend/base/langflow/custom/directory_reader/directory_reader.py +++ b/src/backend/base/langflow/custom/directory_reader/directory_reader.py @@ -324,7 +324,7 @@ class DirectoryReader: tasks = [self.process_file_async(file_path) for file_path in file_paths] results = await asyncio.gather(*tasks) - for file_path, (validation_result, result_content) in zip(file_paths, results): + for file_path, (validation_result, result_content) in zip(file_paths, results, strict=True): menu_name = os.path.basename(os.path.dirname(file_path)) filename = os.path.basename(file_path) diff --git a/src/backend/base/langflow/custom/utils.py b/src/backend/base/langflow/custom/utils.py index ad6e51d16..c5b78e59e 100644 --- a/src/backend/base/langflow/custom/utils.py +++ b/src/backend/base/langflow/custom/utils.py @@ -2,7 +2,6 @@ import ast import contextlib import re import traceback -import warnings from typing import Any from uuid import UUID @@ -177,7 +176,7 @@ def add_new_custom_field( field_config["is_list"] = is_list or field_config.get("list", False) or field_contains_list if "name" in field_config: - warnings.warn("The 'name' key in field_config is used to build the object and can't be changed.") + logger.warning("The 'name' key in field_config is used to build the object and can't be changed.") required = field_config.pop("required", field_required) placeholder = field_config.pop("placeholder", "") diff --git a/src/backend/base/langflow/graph/graph/base.py b/src/backend/base/langflow/graph/graph/base.py index 6ddb6571a..fd6b71587 100644 --- a/src/backend/base/langflow/graph/graph/base.py +++ b/src/backend/base/langflow/graph/graph/base.py @@ -5,7 +5,6 @@ import contextlib import copy import json import uuid -import warnings from collections import defaultdict, deque from collections.abc import Generator, Iterable from datetime import datetime, timezone @@ -263,11 +262,11 @@ class Graph: input_field = target_vertex.get_input(input_name) input_types = input_field.input_types input_field_type = str(input_field.field_type) - except ValueError: + except ValueError as e: input_field = target_vertex.data.get("node", {}).get("template", {}).get(input_name) if not input_field: msg = f"Input field {input_name} not found in target vertex {target_id}" - raise ValueError(msg) + raise ValueError(msg) from e input_types = input_field.get("input_types", []) input_field_type = input_field.get("type", "") @@ -793,7 +792,7 @@ class Graph: types = [] for _ in range(len(inputs) - len(types)): types.append("chat") # default to chat - for run_inputs, components, input_type in zip(inputs, inputs_components, types): + for run_inputs, components, input_type in zip(inputs, inputs_components, types, strict=True): run_outputs = await self._run( inputs=run_inputs, input_components=components, @@ -1199,9 +1198,9 @@ class Graph: """Returns a vertex by id.""" try: return self.vertex_map[vertex_id] - except KeyError: + except KeyError as e: msg = f"Vertex {vertex_id} not found" - raise ValueError(msg) + raise ValueError(msg) from e def get_root_of_group_node(self, vertex_id: str) -> Vertex: """Returns the root of a group node.""" @@ -1646,7 +1645,7 @@ class Graph: new_edge = self.build_edge(edge) edges.add(new_edge) if self.vertices and not edges: - warnings.warn("Graph has vertices but no edges") + logger.warning("Graph has vertices but no edges") return list(cast(Iterable[CycleEdge], edges)) def build_edge(self, edge: EdgeData) -> CycleEdge | Edge: diff --git a/src/backend/base/langflow/graph/graph/state_model.py b/src/backend/base/langflow/graph/graph/state_model.py index b46dce5af..d615c32b9 100644 --- a/src/backend/base/langflow/graph/graph/state_model.py +++ b/src/backend/base/langflow/graph/graph/state_model.py @@ -62,6 +62,6 @@ def create_state_model_from_graph(graph: BaseModel) -> type[BaseModel]: ] fields = { camel_to_snake(vertex.id): state_model_getter - for vertex, state_model_getter in zip(graph.vertices, state_model_getters) + for vertex, state_model_getter in zip(graph.vertices, state_model_getters, strict=False) } return create_state_model(model_name="GraphStateModel", validate=False, **fields) diff --git a/src/backend/base/langflow/graph/graph/utils.py b/src/backend/base/langflow/graph/graph/utils.py index a46029f7f..109c4ba81 100644 --- a/src/backend/base/langflow/graph/graph/utils.py +++ b/src/backend/base/langflow/graph/graph/utils.py @@ -281,14 +281,14 @@ def sort_up_to_vertex( """Cuts the graph up to a given vertex and sorts the resulting subgraph.""" try: stop_or_start_vertex = graph[vertex_id] - except KeyError: + except KeyError as e: if parent_node_map is None: msg = "Parent node map is required to find the root of a group node" - raise ValueError(msg) + raise ValueError(msg) from e vertex_id = get_root_of_group_node(graph=graph, vertex_id=vertex_id, parent_node_map=parent_node_map) if vertex_id not in graph: msg = f"Vertex {vertex_id} not found into graph" - raise ValueError(msg) + raise ValueError(msg) from e stop_or_start_vertex = graph[vertex_id] visited, excluded = set(), set() diff --git a/src/backend/base/langflow/helpers/flow.py b/src/backend/base/langflow/helpers/flow.py index aa1e22a20..492edcfbf 100644 --- a/src/backend/base/langflow/helpers/flow.py +++ b/src/backend/base/langflow/helpers/flow.py @@ -39,7 +39,7 @@ def list_flows(*, user_id: str | None = None) -> list[Data]: return [flow.to_data() for flow in flows] except Exception as e: msg = f"Error listing flows: {e}" - raise ValueError(msg) + raise ValueError(msg) from e async def load_flow( @@ -161,7 +161,7 @@ def generate_function_for_flow( # Map original argument names to their corresponding Pythonic variable names in the function arg_mappings = ", ".join( f'"{original_name}": {name}' - for original_name, name in zip(original_arg_names, [arg.split(":")[0] for arg in args]) + for original_name, name in zip(original_arg_names, [arg.split(":")[0] for arg in args], strict=True) ) func_body = f""" diff --git a/src/backend/base/langflow/initial_setup/setup.py b/src/backend/base/langflow/initial_setup/setup.py index 019a6ba8e..d44c16fc5 100644 --- a/src/backend/base/langflow/initial_setup/setup.py +++ b/src/backend/base/langflow/initial_setup/setup.py @@ -78,7 +78,7 @@ def update_projects_components_with_latest_component_versions(project_data, all_ } ) node_data["template"][key]["value"] = value["value"] - for key, value in node_data["template"].items(): + for key in node_data["template"]: if key not in latest_template: node_data["template"][key]["input_types"] = DEFAULT_PROMPT_INTUT_TYPES node_changes_log[node_data["display_name"]].append( @@ -359,7 +359,7 @@ def load_starter_projects(retries=3, delay=1) -> list[tuple[Path, dict]]: attempt += 1 if attempt >= retries: msg = f"Error loading starter project {file}: {e}" - raise ValueError(msg) + raise ValueError(msg) from e time.sleep(delay) # Wait before retrying return starter_projects diff --git a/src/backend/base/langflow/inputs/inputs.py b/src/backend/base/langflow/inputs/inputs.py index 4270bbb2d..d034385fc 100644 --- a/src/backend/base/langflow/inputs/inputs.py +++ b/src/backend/base/langflow/inputs/inputs.py @@ -108,10 +108,14 @@ class StrInput(BaseInputMixin, ListableInputMixin, DatabaseLoadMixin, MetadataTr if _info.data.get("input_types") and v.__class__.__name__ not in _info.data.get("input_types"): warnings.warn( f"Invalid value type {type(v)} for input {_info.data.get('name')}. " - f"Expected types: {_info.data.get('input_types')}" + f"Expected types: {_info.data.get('input_types')}", + stacklevel=4, ) else: - warnings.warn(f"Invalid value type {type(v)} for input {_info.data.get('name')}.") + warnings.warn( + f"Invalid value type {type(v)} for input {_info.data.get('name')}.", + stacklevel=4, + ) return v @field_validator("value") diff --git a/src/backend/base/langflow/interface/run.py b/src/backend/base/langflow/interface/run.py index d4c58fb1f..e65b9d450 100644 --- a/src/backend/base/langflow/interface/run.py +++ b/src/backend/base/langflow/interface/run.py @@ -36,9 +36,7 @@ def update_memory_keys(langchain_object, possible_new_mem_key): if key not in [langchain_object.memory.memory_key, possible_new_mem_key] ][0] - keys = [input_key, output_key, possible_new_mem_key] - attrs = ["input_key", "output_key", "memory_key"] - for key, attr in zip(keys, attrs): + for key, attr in [(input_key, "input_key"), (output_key, "output_key"), (possible_new_mem_key, "memory_key")]: try: setattr(langchain_object.memory, attr, key) except ValueError as exc: diff --git a/src/backend/base/langflow/load/utils.py b/src/backend/base/langflow/load/utils.py index 5a0a678a7..f4a2d1cb2 100644 --- a/src/backend/base/langflow/load/utils.py +++ b/src/backend/base/langflow/load/utils.py @@ -28,7 +28,7 @@ def upload(file_path, host, flow_id): raise Exception(msg) except Exception as e: msg = f"Error uploading file: {e}" - raise Exception(msg) + raise Exception(msg) from e def upload_file(file_path: str, host: str, flow_id: str, components: list[str], tweaks: dict | None = None): @@ -65,7 +65,7 @@ def upload_file(file_path: str, host: str, flow_id: str, components: list[str], raise ValueError(msg) except Exception as e: msg = f"Error uploading file: {e}" - raise ValueError(msg) + raise ValueError(msg) from e def get_flow(url: str, flow_id: str): @@ -92,4 +92,4 @@ def get_flow(url: str, flow_id: str): raise Exception(msg) except Exception as e: msg = f"Error retrieving flow: {e}" - raise Exception(msg) + raise Exception(msg) from e diff --git a/src/backend/base/langflow/logging/logger.py b/src/backend/base/langflow/logging/logger.py index d56b765f1..a238fa9fa 100644 --- a/src/backend/base/langflow/logging/logger.py +++ b/src/backend/base/langflow/logging/logger.py @@ -78,7 +78,7 @@ class SizedLogBuffer: with self._wlock: as_list = list(self.buffer) max_index = -1 - for i, (ts, msg) in enumerate(as_list): + for i, (ts, _) in enumerate(as_list): if ts >= timestamp: max_index = i break diff --git a/src/backend/base/langflow/memory.py b/src/backend/base/langflow/memory.py index 1a29e0cc7..e477e07b4 100644 --- a/src/backend/base/langflow/memory.py +++ b/src/backend/base/langflow/memory.py @@ -1,4 +1,3 @@ -import warnings from collections.abc import Sequence from uuid import UUID @@ -124,7 +123,7 @@ def store_message( ValueError: If any of the required parameters (session_id, sender, sender_name) is not provided. """ if not message: - warnings.warn("No message provided.") + logger.warning("No message provided.") return [] if not message.session_id or not message.sender or not message.sender_name: diff --git a/src/backend/base/langflow/schema/data.py b/src/backend/base/langflow/schema/data.py index cb22cd06c..c74673559 100644 --- a/src/backend/base/langflow/schema/data.py +++ b/src/backend/base/langflow/schema/data.py @@ -164,10 +164,10 @@ class Data(BaseModel): if key in {"data", "text_key"} or key.startswith("_"): return super().__getattr__(key) return self.data[key] - except KeyError: + except KeyError as e: # Fallback to default behavior to raise AttributeError for undefined attributes msg = f"'{type(self).__name__}' object has no attribute '{key}'" - raise AttributeError(msg) + raise AttributeError(msg) from e def __setattr__(self, key, value): """ diff --git a/src/backend/base/langflow/schema/dotdict.py b/src/backend/base/langflow/schema/dotdict.py index 5941cbd3d..d30defb17 100644 --- a/src/backend/base/langflow/schema/dotdict.py +++ b/src/backend/base/langflow/schema/dotdict.py @@ -29,9 +29,9 @@ class dotdict(dict): value = dotdict(value) self[attr] = value # Update self to nest dotdict for future accesses return value - except KeyError: + except KeyError as e: msg = f"'dotdict' object has no attribute '{attr}'" - raise AttributeError(msg) + raise AttributeError(msg) from e def __setattr__(self, key, value): """ @@ -57,9 +57,9 @@ class dotdict(dict): """ try: del self[key] - except KeyError: + except KeyError as e: msg = f"'dotdict' object has no attribute '{key}'" - raise AttributeError(msg) + raise AttributeError(msg) from e def __missing__(self, key): """ diff --git a/src/backend/base/langflow/schema/message.py b/src/backend/base/langflow/schema/message.py index aa31ba9b7..bf44627e7 100644 --- a/src/backend/base/langflow/schema/message.py +++ b/src/backend/base/langflow/schema/message.py @@ -31,9 +31,9 @@ def _timestamp_to_str(timestamp: datetime | str) -> str: try: datetime.strptime(timestamp, "%Y-%m-%d %H:%M:%S") return timestamp - except ValueError: + except ValueError as e: msg = f"Invalid timestamp: {timestamp}" - raise ValueError(msg) + raise ValueError(msg) from e return timestamp.strftime("%Y-%m-%d %H:%M:%S") diff --git a/src/backend/base/langflow/services/base.py b/src/backend/base/langflow/services/base.py index 430ecd11e..dc1a9882d 100644 --- a/src/backend/base/langflow/services/base.py +++ b/src/backend/base/langflow/services/base.py @@ -23,7 +23,7 @@ class Service(ABC): return schema async def teardown(self): - pass + return def set_ready(self): self.ready = True diff --git a/src/backend/base/langflow/services/database/models/flow/model.py b/src/backend/base/langflow/services/database/models/flow/model.py index cd15ce6de..1898c94f3 100644 --- a/src/backend/base/langflow/services/database/models/flow/model.py +++ b/src/backend/base/langflow/services/database/models/flow/model.py @@ -1,7 +1,6 @@ # Path: src/backend/langflow/services/database/models/flow/model.py import re -import warnings from datetime import datetime, timezone from typing import TYPE_CHECKING, Optional from uuid import UUID, uuid4 @@ -9,6 +8,7 @@ from uuid import UUID, uuid4 import emoji from emoji import purely_emoji # type: ignore from fastapi import HTTPException, status +from loguru import logger from pydantic import field_serializer, field_validator from sqlalchemy import Text, UniqueConstraint from sqlmodel import JSON, Column, Field, Relationship, SQLModel @@ -88,8 +88,7 @@ class FlowBase(SQLModel): emoji_value = emoji.emojize(v, variant="emoji_type") if v == emoji_value: - warnings.warn(f"Invalid emoji. {v} is not a valid emoji.") - icon = v + logger.warning(f"Invalid emoji. {v} is not a valid emoji.") icon = emoji_value if purely_emoji(icon): diff --git a/src/backend/base/langflow/services/database/service.py b/src/backend/base/langflow/services/database/service.py index c4d0667b8..fd5b51291 100644 --- a/src/backend/base/langflow/services/database/service.py +++ b/src/backend/base/langflow/services/database/service.py @@ -220,7 +220,7 @@ class DatabaseService(Service): logger.error(f"AutogenerateDiffsDetected: {exc}") if not fix: msg = f"There's a mismatch between the models and the database.\n{exc}" - raise RuntimeError(msg) + raise RuntimeError(msg) from exc if fix: self.try_downgrade_upgrade_until_success(alembic_cfg) diff --git a/src/backend/base/langflow/services/factory.py b/src/backend/base/langflow/services/factory.py index 86db0daa7..ea4fa3bbc 100644 --- a/src/backend/base/langflow/services/factory.py +++ b/src/backend/base/langflow/services/factory.py @@ -54,9 +54,9 @@ def infer_service_types(factory_class: type[ServiceFactory], available_services= # Attempt to find a matching enum value service_type = ServiceType[type_name] service_types.append(service_type) - except KeyError: + except KeyError as e: msg = f"No matching ServiceType for parameter type: {param_type.__name__}" - raise ValueError(msg) + raise ValueError(msg) from e return service_types diff --git a/src/backend/base/langflow/services/manager.py b/src/backend/base/langflow/services/manager.py index 401bd4b0d..21ba7429d 100644 --- a/src/backend/base/langflow/services/manager.py +++ b/src/backend/base/langflow/services/manager.py @@ -132,7 +132,7 @@ class ServiceManager: module = importlib.import_module(module_name) # Find all classes in the module that are subclasses of ServiceFactory - for name, obj in inspect.getmembers(module, inspect.isclass): + for _, obj in inspect.getmembers(module, inspect.isclass): if issubclass(obj, ServiceFactory) and obj is not ServiceFactory: factories.append(obj()) break diff --git a/src/backend/base/langflow/services/store/service.py b/src/backend/base/langflow/services/store/service.py index c76392e9d..c6f60ab6a 100644 --- a/src/backend/base/langflow/services/store/service.py +++ b/src/backend/base/langflow/services/store/service.py @@ -44,7 +44,7 @@ async def user_data_context(store_service: StoreService, api_key: str | None = N except HTTPStatusError as exc: if exc.response.status_code == 403: msg = "Invalid API key" - raise ValueError(msg) + raise ValueError(msg) from exc try: yield finally: @@ -119,10 +119,10 @@ class StoreService(Service): if exc.response.status_code in [403, 401]: return False msg = f"Unexpected status code: {exc.response.status_code}" - raise ValueError(msg) + raise ValueError(msg) from exc except Exception as exc: msg = f"Unexpected error: {exc}" - raise ValueError(msg) + raise ValueError(msg) from exc async def _get( self, url: str, api_key: str | None = None, params: dict[str, Any] | None = None @@ -137,7 +137,7 @@ class StoreService(Service): raise exc except Exception as exc: msg = f"GET failed: {exc}" - raise ValueError(msg) + raise ValueError(msg) from exc json_response = response.json() result = json_response["data"] metadata = {} @@ -355,9 +355,9 @@ class StoreService(Service): # If it is, we need to build the metadata try: download_component.metadata = process_component_data(download_component.data.get("nodes", [])) - except KeyError: + except KeyError as e: msg = "Invalid component data. No nodes found" - raise ValueError(msg) + raise ValueError(msg) from e return download_component async def upload(self, api_key: str, component_data: StoreComponentCreate) -> CreateComponentResponse: @@ -392,7 +392,7 @@ class StoreService(Service): except UnboundLocalError: pass msg = f"Upload failed: {exc}" - raise ValueError(msg) + raise ValueError(msg) from exc async def update( self, api_key: str, component_id: UUID, component_data: StoreComponentCreate @@ -429,7 +429,7 @@ class StoreService(Service): except UnboundLocalError: pass msg = f"Upload failed: {exc}" - raise ValueError(msg) + raise ValueError(msg) from exc async def get_tags(self) -> list[dict[str, Any]]: url = f"{self.base_url}/items/tags" @@ -460,9 +460,9 @@ class StoreService(Service): # try to convert it to int try: likes = int(likes) - except ValueError: + except ValueError as e: msg = f"Unexpected value for likes count: {likes}" - raise ValueError(msg) + raise ValueError(msg) from e return likes async def like_component(self, api_key: str, component_id: str) -> bool: @@ -567,10 +567,10 @@ class StoreService(Service): except HTTPStatusError as exc: if exc.response.status_code == 403: msg = "You are not authorized to access this public resource" - raise ForbiddenError(msg) + raise ForbiddenError(msg) from exc if exc.response.status_code == 401: msg = "You are not authorized to access this resource. Please check your API key." - raise APIKeyError(msg) + raise APIKeyError(msg) from exc if store_api_key: # Now, from the result, we need to get the components diff --git a/src/backend/base/langflow/services/tracing/base.py b/src/backend/base/langflow/services/tracing/base.py index f4bda77f1..6e39b643d 100644 --- a/src/backend/base/langflow/services/tracing/base.py +++ b/src/backend/base/langflow/services/tracing/base.py @@ -1,6 +1,7 @@ from __future__ import annotations from abc import ABC, abstractmethod +from collections.abc import Sequence from typing import TYPE_CHECKING, Any from uuid import UUID @@ -40,7 +41,7 @@ class BaseTracer(ABC): trace_name: str, outputs: dict[str, Any] | None = None, error: Exception | None = None, - logs: list[Log | dict] = [], + logs: Sequence[Log | dict] = (), ): raise NotImplementedError diff --git a/src/backend/base/langflow/services/tracing/langfuse.py b/src/backend/base/langflow/services/tracing/langfuse.py index 8560c2732..2ca8b7abc 100644 --- a/src/backend/base/langflow/services/tracing/langfuse.py +++ b/src/backend/base/langflow/services/tracing/langfuse.py @@ -1,6 +1,7 @@ from __future__ import annotations import os +from collections.abc import Sequence from datetime import datetime from typing import TYPE_CHECKING, Any from uuid import UUID @@ -100,7 +101,7 @@ class LangFuseTracer(BaseTracer): trace_name: str, outputs: dict[str, Any] | None = None, error: Exception | None = None, - logs: list[Log | dict] = [], + logs: Sequence[Log | dict] = (), ): end_time = datetime.utcnow() if not self._ready: @@ -111,7 +112,7 @@ class LangFuseTracer(BaseTracer): _output: dict = {} _output |= outputs if outputs else {} _output |= {"error": str(error)} if error else {} - _output |= {"logs": logs} if logs else {} + _output |= {"logs": list(logs)} if logs else {} content = {"output": _output, "end_time": end_time} span.update(**content) diff --git a/src/backend/base/langflow/services/tracing/langsmith.py b/src/backend/base/langflow/services/tracing/langsmith.py index 209f7fd5f..4cdbe36c0 100644 --- a/src/backend/base/langflow/services/tracing/langsmith.py +++ b/src/backend/base/langflow/services/tracing/langsmith.py @@ -3,6 +3,7 @@ from __future__ import annotations import os import traceback import types +from collections.abc import Sequence from datetime import datetime, timezone from typing import TYPE_CHECKING, Any from uuid import UUID @@ -119,7 +120,7 @@ class LangSmithTracer(BaseTracer): trace_name: str, outputs: dict[str, Any] | None = None, error: Exception | None = None, - logs: list[Log | dict] = [], + logs: Sequence[Log | dict] = (), ): if not self._ready: return diff --git a/src/backend/base/langflow/services/tracing/langwatch.py b/src/backend/base/langflow/services/tracing/langwatch.py index 266cdc6f4..baaabcb12 100644 --- a/src/backend/base/langflow/services/tracing/langwatch.py +++ b/src/backend/base/langflow/services/tracing/langwatch.py @@ -1,5 +1,6 @@ from __future__ import annotations +from collections.abc import Sequence from typing import TYPE_CHECKING, Any, cast from uuid import UUID @@ -103,7 +104,7 @@ class LangWatchTracer(BaseTracer): trace_name: str, outputs: dict[str, Any] | None = None, error: Exception | None = None, - logs: list[Log | dict] = [], + logs: Sequence[Log | dict] = (), ): if not self._ready: return diff --git a/src/backend/base/langflow/services/variable/service.py b/src/backend/base/langflow/services/variable/service.py index 77e44a9a1..e29298864 100644 --- a/src/backend/base/langflow/services/variable/service.py +++ b/src/backend/base/langflow/services/variable/service.py @@ -1,6 +1,7 @@ from __future__ import annotations import os +from collections.abc import Sequence from datetime import datetime, timezone from typing import TYPE_CHECKING from uuid import UUID @@ -165,7 +166,7 @@ class DatabaseVariableService(VariableService, Service): user_id: UUID | str, name: str, value: str, - default_fields: list[str] = [], + default_fields: Sequence[str] = (), _type: str = GENERIC_TYPE, session: Session = Depends(get_session), ): @@ -173,7 +174,7 @@ class DatabaseVariableService(VariableService, Service): name=name, type=_type, value=auth_utils.encrypt_api_key(value, settings_service=self.settings_service), - default_fields=default_fields, + default_fields=list(default_fields), ) variable = Variable.model_validate(variable_base, from_attributes=True, update={"user_id": user_id}) session.add(variable) diff --git a/src/backend/base/langflow/template/template/base.py b/src/backend/base/langflow/template/template/base.py index 76004bd99..9600e84e0 100644 --- a/src/backend/base/langflow/template/template/base.py +++ b/src/backend/base/langflow/template/template/base.py @@ -53,7 +53,7 @@ class Template(BaseModel): _input = instantiate_input(input_type, value) except Exception as e: msg = f"Error instantiating input {input_type}: {e}" - raise ValueError(msg) + raise ValueError(msg) from e else: _input = Input(**value) diff --git a/src/backend/base/langflow/utils/validate.py b/src/backend/base/langflow/utils/validate.py index 7ee3530fe..17dc41965 100644 --- a/src/backend/base/langflow/utils/validate.py +++ b/src/backend/base/langflow/utils/validate.py @@ -217,9 +217,9 @@ def prepare_global_scope(code, module): imported_module = importlib.import_module(node.module) for alias in node.names: exec_globals[alias.name] = getattr(imported_module, alias.name) - except ModuleNotFoundError: + except ModuleNotFoundError as e: msg = f"Module {node.module} not found. Please install it and try again" - raise ModuleNotFoundError(msg) + raise ModuleNotFoundError(msg) from e return exec_globals diff --git a/src/backend/base/langflow/utils/version.py b/src/backend/base/langflow/utils/version.py index 262edd649..de14fc679 100644 --- a/src/backend/base/langflow/utils/version.py +++ b/src/backend/base/langflow/utils/version.py @@ -74,20 +74,18 @@ def fetch_latest_version(package_name: str, include_prerelease: bool) -> str | N from packaging import version as pkg_version package_name = package_name.replace(" ", "-").lower() - valid_versions = [] try: response = httpx.get(f"https://pypi.org/pypi/{package_name}/json") versions = response.json()["releases"].keys() valid_versions = [v for v in versions if include_prerelease or not is_pre_release(v)] - - except Exception as e: - logger.exception(e) - - finally: if not valid_versions: return None # Handle case where no valid versions are found return max(valid_versions, key=lambda v: pkg_version.parse(v)) + except Exception as e: + logger.exception(e) + return None + def get_version_info(): return VERSION_INFO diff --git a/src/backend/base/pyproject.toml b/src/backend/base/pyproject.toml index 9a4783969..d6c4106ba 100644 --- a/src/backend/base/pyproject.toml +++ b/src/backend/base/pyproject.toml @@ -149,8 +149,15 @@ exclude = ["langflow/alembic"] line-length = 120 [tool.ruff.lint] +flake8-bugbear.extend-immutable-calls = [ + "fastapi.Depends", + "fastapi.File", + "fastapi.Query", + "typer.Option", +] select = [ "ASYNC", + "B", "C4", "COM", "DJ", diff --git a/src/backend/tests/conftest.py b/src/backend/tests/conftest.py index ced3f5e6d..8bcfae4ea 100644 --- a/src/backend/tests/conftest.py +++ b/src/backend/tests/conftest.py @@ -11,6 +11,9 @@ from typing import TYPE_CHECKING import orjson import pytest +from loguru import logger +from pytest import LogCaptureFixture + from base.langflow.components.inputs.ChatInput import ChatInput from dotenv import load_dotenv from fastapi.testclient import TestClient @@ -79,6 +82,19 @@ def get_text(): assert path.exists(), f"File {path} does not exist. Available files: {list(data_path.iterdir())}" +@pytest.fixture +def caplog(caplog: LogCaptureFixture): + handler_id = logger.add( + caplog.handler, + format="{message}", + level=0, + filter=lambda record: record["level"].no >= caplog.handler.level, + enqueue=False, # Set to 'True' if your test is spawning child processes. + ) + yield caplog + logger.remove(handler_id) + + @pytest.fixture() async def async_client() -> AsyncGenerator: from langflow.main import create_app diff --git a/src/backend/tests/unit/graph/graph/test_base.py b/src/backend/tests/unit/graph/graph/test_base.py index 9ca9df5bc..8a7e32814 100644 --- a/src/backend/tests/unit/graph/graph/test_base.py +++ b/src/backend/tests/unit/graph/graph/test_base.py @@ -1,6 +1,8 @@ +import logging from collections import deque import pytest +from pytest import LogCaptureFixture from langflow.components.agents.ToolCallingAgent import ToolCallingAgentComponent from langflow.components.inputs.ChatInput import ChatInput @@ -28,14 +30,16 @@ async def test_graph_not_prepared(): @pytest.mark.asyncio -async def test_graph(): +async def test_graph(caplog: LogCaptureFixture): chat_input = ChatInput() chat_output = ChatOutput() graph = Graph() graph.add_component(chat_input) graph.add_component(chat_output) - with pytest.warns(UserWarning, match="Graph has vertices but no edges"): + caplog.clear() + with caplog.at_level(logging.WARNING): graph.prepare() + assert "Graph has vertices but no edges" in caplog.text @pytest.mark.asyncio