From f8a2a7d3b8b313f65a0edb109c758f7086333286 Mon Sep 17 00:00:00 2001 From: Christophe Bornet Date: Wed, 25 Sep 2024 19:38:50 +0200 Subject: [PATCH] feat: Activate ruff rules UP(pyupgrade) (#3871) * Activate ruff rules UP(pyupgrade) * Set ruff target version * Fix threading.Lock | None error Due to https://github.com/python/cpython/issues/114315 * Fix Text in custom component supported typed * Fix mypy issues --------- Co-authored-by: Gabriel Luiz Freitas Almeida --- src/backend/base/langflow/__main__.py | 7 +- src/backend/base/langflow/api/utils.py | 8 +- src/backend/base/langflow/api/v1/callback.py | 4 +- src/backend/base/langflow/api/v1/chat.py | 30 ++++--- src/backend/base/langflow/api/v1/endpoints.py | 12 +-- .../base/langflow/base/agents/agent.py | 8 +- .../base/langflow/base/agents/callback.py | 14 +-- .../base/langflow/base/agents/crewai/crew.py | 5 +- .../base/langflow/base/agents/utils.py | 33 +++---- src/backend/base/langflow/base/curl/parse.py | 8 +- src/backend/base/langflow/base/data/utils.py | 16 ++-- .../langflow/base/flow_processing/utils.py | 8 +- src/backend/base/langflow/base/io/chat.py | 16 ++-- .../base/langchain_utilities/model.py | 4 +- .../base/langflow/base/memory/memory.py | 4 +- .../base/langflow/base/models/model.py | 9 +- .../base/langflow/base/prompts/api_utils.py | 4 +- .../base/langflow/base/tools/flow_tool.py | 14 +-- .../base/langflow/base/vectorstores/model.py | 8 +- .../components/Notion/add_content_to_page.py | 6 +- .../langflow/components/Notion/create_page.py | 4 +- .../Notion/list_database_properties.py | 4 +- .../langflow/components/Notion/list_pages.py | 8 +- .../langflow/components/Notion/list_users.py | 6 +- .../base/langflow/components/Notion/search.py | 6 +- .../components/Notion/update_page_property.py | 6 +- .../langflow/components/agents/JsonAgent.py | 2 +- .../components/agents/OpenAIToolsAgent.py | 4 +- .../components/agents/OpenAPIAgent.py | 2 +- .../components/agents/ToolCallingAgent.py | 4 +- .../langflow/components/agents/XMLAgent.py | 4 +- .../components/astra_assistants/run.py | 4 +- .../langflow/components/data/APIRequest.py | 8 +- .../langflow/components/data/Directory.py | 4 +- .../base/langflow/components/data/Gmail.py | 9 +- .../langflow/components/data/GoogleDrive.py | 3 +- .../components/data/GoogleDriveSearch.py | 7 +- .../components/deactivated/DocumentsToData.py | 4 +- .../components/deactivated/ListFlows.py | 4 +- .../components/deactivated/Message.py | 6 +- .../components/deactivated/SplitText.py | 4 +- .../components/deactivated/SubFlow.py | 12 +-- .../components/documentloaders/Confluence.py | 4 +- .../components/documentloaders/GitLoader.py | 3 +- .../documentloaders/Unstructured.py | 4 +- .../embeddings/EmbeddingSimilarity.py | 4 +- .../GoogleGenerativeAIEmbeddings.py | 30 +++---- .../embeddings/util/AIMLEmbeddingsImpl.py | 5 +- .../langflow/components/helpers/FilterData.py | 4 +- .../components/helpers/IDGenerator.py | 4 +- .../langflow/components/helpers/SplitText.py | 4 +- .../langchain_utilities/FirecrawlCrawlApi.py | 7 +- .../langchain_utilities/FirecrawlScrapeApi.py | 6 +- .../langflow/components/models/GroqModel.py | 4 +- .../components/models/HuggingFaceModel.py | 16 ++-- .../components/prompts/LangChainHubPrompt.py | 5 +- .../components/prototypes/FlowTool.py | 6 +- .../langflow/components/prototypes/Notify.py | 4 +- .../components/prototypes/PythonFunction.py | 6 +- .../langflow/components/prototypes/RunFlow.py | 8 +- .../langflow/components/prototypes/SubFlow.py | 10 +-- .../components/retrievers/AmazonKendra.py | 10 +-- .../components/retrievers/CohereRerank.py | 4 +- .../components/retrievers/MetalRetriever.py | 4 +- .../retrievers/MultiQueryRetriever.py | 4 +- .../components/retrievers/NvidiaRerank.py | 4 +- .../retrievers/SelfQueryRetriever.py | 4 +- .../retrievers/VectaraSelfQueryRetriver.py | 4 +- .../components/toolkits/ComposioAPI.py | 3 +- .../langflow/components/toolkits/Metaphor.py | 6 +- .../components/tools/BingSearchAPI.py | 4 +- .../langflow/components/tools/Calculator.py | 5 +- .../components/tools/DuckDuckGoSearchRun.py | 6 +- .../components/tools/GleanSearchAPI.py | 8 +- .../components/tools/GoogleSearchAPI.py | 4 +- .../components/tools/GoogleSerperAPI.py | 4 +- .../components/tools/PythonREPLTool.py | 5 +- .../langflow/components/tools/SearchAPI.py | 10 +-- .../base/langflow/components/tools/SerpAPI.py | 10 +-- .../components/vectorstores/Cassandra.py | 4 +- .../components/vectorstores/CassandraGraph.py | 3 +- .../components/vectorstores/Clickhouse.py | 4 +- .../components/vectorstores/Couchbase.py | 3 +- .../langflow/components/vectorstores/FAISS.py | 4 +- .../components/vectorstores/Milvus.py | 4 +- .../vectorstores/MongoDBAtlasVector.py | 4 +- .../components/vectorstores/Pinecone.py | 4 +- .../components/vectorstores/Qdrant.py | 4 +- .../langflow/components/vectorstores/Redis.py | 4 +- .../vectorstores/SupabaseVectorStore.py | 4 +- .../components/vectorstores/Upstash.py | 4 +- .../components/vectorstores/Vectara.py | 4 +- .../components/vectorstores/Weaviate.py | 4 +- .../components/vectorstores/pgvector.py | 4 +- .../custom/code_parser/code_parser.py | 6 +- .../custom/custom_component/component.py | 16 ++-- .../custom_component/custom_component.py | 14 +-- .../base/langflow/field_typing/constants.py | 7 +- src/backend/base/langflow/graph/graph/base.py | 78 ++++++++-------- .../langflow/graph/graph/state_manager.py | 4 +- src/backend/base/langflow/graph/utils.py | 6 +- .../base/langflow/graph/vertex/base.py | 6 +- .../base/langflow/graph/vertex/types.py | 12 +-- .../base/langflow/graph/vertex/utils.py | 4 +- src/backend/base/langflow/helpers/data.py | 6 +- src/backend/base/langflow/helpers/flow.py | 37 ++++---- .../base/langflow/initial_setup/setup.py | 6 +- src/backend/base/langflow/inputs/inputs.py | 60 ++++++------- .../langflow/interface/initialize/loading.py | 22 ++--- src/backend/base/langflow/interface/utils.py | 2 +- src/backend/base/langflow/load/load.py | 37 ++++---- src/backend/base/langflow/logging/logger.py | 10 +-- src/backend/base/langflow/main.py | 3 +- src/backend/base/langflow/memory.py | 6 +- src/backend/base/langflow/processing/base.py | 4 +- .../base/langflow/processing/process.py | 32 +++---- src/backend/base/langflow/schema/artifact.py | 4 +- src/backend/base/langflow/schema/data.py | 4 +- src/backend/base/langflow/schema/graph.py | 10 +-- src/backend/base/langflow/schema/image.py | 2 +- src/backend/base/langflow/schema/log.py | 6 +- src/backend/base/langflow/schema/message.py | 17 ++-- src/backend/base/langflow/schema/schema.py | 9 +- src/backend/base/langflow/schema/table.py | 5 +- .../base/langflow/services/auth/utils.py | 11 +-- .../base/langflow/services/cache/base.py | 22 ++--- .../base/langflow/services/cache/disk.py | 14 +-- .../base/langflow/services/cache/service.py | 24 ++--- .../base/langflow/services/cache/utils.py | 4 +- .../base/langflow/services/chat/cache.py | 11 +-- .../base/langflow/services/chat/service.py | 10 +-- .../services/database/models/api_key/crud.py | 7 +- .../services/database/models/api_key/model.py | 14 +-- .../services/database/models/flow/model.py | 48 +++++----- .../services/database/models/flow/utils.py | 4 +- .../services/database/models/folder/model.py | 34 +++---- .../services/database/models/message/model.py | 18 ++-- .../database/models/transactions/crud.py | 3 +- .../database/models/transactions/model.py | 12 +-- .../services/database/models/user/crud.py | 7 +- .../services/database/models/user/model.py | 24 ++--- .../database/models/variable/model.py | 32 +++---- .../database/models/vertex_builds/crud.py | 3 +- .../database/models/vertex_builds/model.py | 10 +-- .../langflow/services/database/service.py | 8 +- .../base/langflow/services/database/utils.py | 10 +-- src/backend/base/langflow/services/deps.py | 3 +- src/backend/base/langflow/services/factory.py | 8 +- src/backend/base/langflow/services/manager.py | 16 ++-- .../base/langflow/services/monitor/service.py | 4 +- .../base/langflow/services/monitor/utils.py | 12 +-- .../services/plugins/langfuse_plugin.py | 2 +- .../base/langflow/services/plugins/service.py | 4 +- .../base/langflow/services/session/service.py | 6 +- .../base/langflow/services/settings/base.py | 36 ++++---- .../langflow/services/settings/manager.py | 2 +- .../base/langflow/services/socket/utils.py | 2 +- .../base/langflow/services/state/service.py | 2 +- .../base/langflow/services/store/schema.py | 59 ++++++------- .../base/langflow/services/store/service.py | 88 +++++++++---------- .../base/langflow/services/store/utils.py | 4 +- .../langflow/services/task/backends/anyio.py | 5 +- .../langflow/services/task/backends/base.py | 3 +- .../langflow/services/task/backends/celery.py | 3 +- .../base/langflow/services/task/service.py | 3 +- .../services/telemetry/opentelemetry.py | 15 ++-- .../base/langflow/services/tracing/base.py | 10 +-- .../langflow/services/tracing/langfuse.py | 10 +-- .../langflow/services/tracing/langsmith.py | 14 +-- .../langflow/services/tracing/langwatch.py | 20 +++-- .../base/langflow/services/tracing/service.py | 18 ++-- .../base/langflow/services/tracing/utils.py | 4 +- .../base/langflow/services/variable/base.py | 15 ++-- .../langflow/services/variable/kubernetes.py | 21 +++-- .../services/variable/kubernetes_secrets.py | 3 +- .../langflow/services/variable/service.py | 22 ++--- .../base/langflow/template/field/base.py | 2 +- .../type_extraction/type_extraction.py | 4 +- src/backend/base/langflow/utils/constants.py | 4 +- src/backend/base/langflow/utils/payload.py | 3 +- src/backend/base/langflow/utils/schemas.py | 21 +++-- src/backend/base/langflow/utils/util.py | 34 +++---- src/backend/base/langflow/utils/validate.py | 8 +- src/backend/base/langflow/utils/version.py | 4 +- src/backend/base/langflow/worker.py | 8 +- src/backend/base/pyproject.toml | 3 +- 186 files changed, 911 insertions(+), 983 deletions(-) diff --git a/src/backend/base/langflow/__main__.py b/src/backend/base/langflow/__main__.py index 7e3834e93..fb8c01512 100644 --- a/src/backend/base/langflow/__main__.py +++ b/src/backend/base/langflow/__main__.py @@ -4,7 +4,6 @@ import sys import time import warnings from pathlib import Path -from typing import Optional import click import httpx @@ -84,7 +83,7 @@ def run( workers: int = typer.Option(1, help="Number of worker processes.", envvar="LANGFLOW_WORKERS"), timeout: int = typer.Option(300, help="Worker timeout in seconds.", envvar="LANGFLOW_WORKER_TIMEOUT"), port: int = typer.Option(7860, help="Port to listen on.", envvar="LANGFLOW_PORT"), - components_path: Optional[Path] = typer.Option( + components_path: Path | None = typer.Option( Path(__file__).parent / "components", help="Path to the directory containing custom components.", envvar="LANGFLOW_COMPONENTS_PATH", @@ -93,7 +92,7 @@ def run( env_file: Path = typer.Option(None, help="Path to the .env file containing environment variables."), log_level: str = typer.Option("critical", help="Logging level.", envvar="LANGFLOW_LOG_LEVEL"), log_file: Path = typer.Option("logs/langflow.log", help="Path to the log file.", envvar="LANGFLOW_LOG_FILE"), - cache: Optional[str] = typer.Option( + cache: str | None = typer.Option( envvar="LANGFLOW_LANGCHAIN_CACHE", help="Type of cache to use. (InMemoryCache, SQLiteCache)", default=None, @@ -161,7 +160,7 @@ def run( health_check_max_retries=health_check_max_retries, ) # create path object if path is provided - static_files_dir: Optional[Path] = Path(path) if path else None + static_files_dir: Path | None = Path(path) if path else None settings_service = get_settings_service() settings_service.set("backend_only", backend_only) app = setup_app(static_files_dir=static_files_dir, backend_only=backend_only) diff --git a/src/backend/base/langflow/api/utils.py b/src/backend/base/langflow/api/utils.py index 5d55dfde6..1cc306d23 100644 --- a/src/backend/base/langflow/api/utils.py +++ b/src/backend/base/langflow/api/utils.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import uuid import warnings from typing import TYPE_CHECKING, Any @@ -69,7 +71,7 @@ def build_input_keys_response(langchain_object, artifacts): return input_keys_response -def validate_is_component(flows: list["Flow"]): +def validate_is_component(flows: list[Flow]): for flow in flows: if not flow.data or flow.is_component is not None: continue @@ -152,7 +154,7 @@ async def build_graph_from_db_no_cache(flow_id: str, session: Session): return await build_graph_from_data(flow_id, flow.data, flow_name=flow.name, user_id=str(flow.user_id)) -async def build_graph_from_db(flow_id: str, session: Session, chat_service: "ChatService"): +async def build_graph_from_db(flow_id: str, session: Session, chat_service: ChatService): graph = await build_graph_from_db_no_cache(flow_id, session) await chat_service.set_cache(flow_id, graph) return graph @@ -160,7 +162,7 @@ async def build_graph_from_db(flow_id: str, session: Session, chat_service: "Cha async def build_and_cache_graph_from_data( flow_id: str, - chat_service: "ChatService", + chat_service: ChatService, graph_data: dict, ): # -> Graph | Any: """Build and cache the graph.""" diff --git a/src/backend/base/langflow/api/v1/callback.py b/src/backend/base/langflow/api/v1/callback.py index d6a1a1148..a075e482e 100644 --- a/src/backend/base/langflow/api/v1/callback.py +++ b/src/backend/base/langflow/api/v1/callback.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from typing import TYPE_CHECKING, Any from uuid import UUID @@ -25,7 +27,7 @@ class AsyncStreamingLLMCallbackHandleSIO(AsyncCallbackHandler): def __init__(self, session_id: str): self.chat_service = get_chat_service() self.client_id = session_id - self.socketio_service: "SocketIOService" = get_socket_service() + self.socketio_service: SocketIOService = get_socket_service() self.sid = session_id # self.socketio_service = self.chat_service.active_connections[self.client_id] diff --git a/src/backend/base/langflow/api/v1/chat.py b/src/backend/base/langflow/api/v1/chat.py index b0f7d477f..00afd0d7d 100644 --- a/src/backend/base/langflow/api/v1/chat.py +++ b/src/backend/base/langflow/api/v1/chat.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import asyncio import json import time @@ -72,9 +74,9 @@ async def retrieve_vertices_order( data: Annotated[FlowDataRequest | None, Body(embed=True)] | None = None, stop_component_id: str | None = None, start_component_id: str | None = None, - chat_service: "ChatService" = Depends(get_chat_service), + chat_service: ChatService = Depends(get_chat_service), session=Depends(get_session), - telemetry_service: "TelemetryService" = Depends(get_telemetry_service), + telemetry_service: TelemetryService = Depends(get_telemetry_service), ): """ Retrieve the vertices order for a given flow. @@ -148,12 +150,12 @@ async def build_flow( stop_component_id: str | None = None, start_component_id: str | None = None, log_builds: bool | None = True, - chat_service: "ChatService" = Depends(get_chat_service), + chat_service: ChatService = Depends(get_chat_service), current_user=Depends(get_current_active_user), - telemetry_service: "TelemetryService" = Depends(get_telemetry_service), + telemetry_service: TelemetryService = Depends(get_telemetry_service), session=Depends(get_session), ): - async def build_graph_and_get_order() -> tuple[list[str], list[str], "Graph"]: + async def build_graph_and_get_order() -> tuple[list[str], list[str], Graph]: start_time = time.perf_counter() components_count = None try: @@ -205,7 +207,7 @@ async def build_flow( logger.exception(exc) raise HTTPException(status_code=500, detail=str(exc)) from exc - async def _build_vertex(vertex_id: str, graph: "Graph", event_manager: "EventManager") -> VertexBuildResponse: + async def _build_vertex(vertex_id: str, graph: Graph, event_manager: EventManager) -> VertexBuildResponse: flow_id_str = str(flow_id) next_runnable_vertices = [] @@ -320,9 +322,9 @@ async def build_flow( async def build_vertices( vertex_id: str, - graph: "Graph", + graph: Graph, client_consumed_queue: asyncio.Queue, - event_manager: "EventManager", + event_manager: EventManager, ) -> None: build_task = asyncio.create_task(asyncio.to_thread(asyncio.run, _build_vertex(vertex_id, graph, event_manager))) try: @@ -457,9 +459,9 @@ async def build_vertex( background_tasks: BackgroundTasks, inputs: Annotated[InputValueRequest | None, Body(embed=True)] = None, files: list[str] | None = None, - chat_service: "ChatService" = Depends(get_chat_service), + chat_service: ChatService = Depends(get_chat_service), current_user=Depends(get_current_active_user), - telemetry_service: "TelemetryService" = Depends(get_telemetry_service), + telemetry_service: TelemetryService = Depends(get_telemetry_service), ): """Build a vertex instead of the entire graph. @@ -489,7 +491,7 @@ async def build_vertex( if not cache: # If there's no cache logger.warning(f"No cache found for {flow_id_str}. Building graph starting at {vertex_id}") - graph: "Graph" = await build_graph_from_db( + graph: Graph = await build_graph_from_db( flow_id=flow_id_str, session=next(get_session()), chat_service=chat_service ) else: @@ -609,8 +611,8 @@ async def build_vertex_stream( flow_id: uuid.UUID, vertex_id: str, session_id: str | None = None, - chat_service: "ChatService" = Depends(get_chat_service), - session_service: "SessionService" = Depends(get_session_service), + chat_service: ChatService = Depends(get_chat_service), + session_service: SessionService = Depends(get_session_service), ): """Build a vertex instead of the entire graph. @@ -650,7 +652,7 @@ async def build_vertex_stream( else: graph = cache.get("result") - vertex: "InterfaceVertex" = graph.get_vertex(vertex_id) + vertex: InterfaceVertex = graph.get_vertex(vertex_id) if not hasattr(vertex, "stream"): raise ValueError(f"Vertex {vertex_id} does not support streaming") if isinstance(vertex._built_result, str) and vertex._built_result: diff --git a/src/backend/base/langflow/api/v1/endpoints.py b/src/backend/base/langflow/api/v1/endpoints.py index 78960f456..608be7874 100644 --- a/src/backend/base/langflow/api/v1/endpoints.py +++ b/src/backend/base/langflow/api/v1/endpoints.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import time from asyncio import Lock from http import HTTPStatus @@ -62,7 +64,7 @@ router = APIRouter(tags=["Base"]) @router.get("/all", dependencies=[Depends(get_current_active_user)]) async def get_all( settings_service=Depends(get_settings_service), - cache_service: "CacheService" = Depends(dependency=get_cache_service), + cache_service: CacheService = Depends(dependency=get_cache_service), force_refresh: bool = False, ): from langflow.interface.types import get_and_cache_all_types_dict @@ -181,7 +183,7 @@ async def simplified_run_flow( input_request: SimplifiedAPIRequest = SimplifiedAPIRequest(), stream: bool = False, api_key_user: UserRead = Depends(api_key_security), - telemetry_service: "TelemetryService" = Depends(get_telemetry_service), + telemetry_service: TelemetryService = Depends(get_telemetry_service), ): """ Executes a specified flow by ID with input customization, performance enhancements through caching, and optional data streaming. @@ -290,7 +292,7 @@ async def webhook_run_flow( user: Annotated[User, Depends(get_user_by_flow_id_or_endpoint_name)], request: Request, background_tasks: BackgroundTasks, - telemetry_service: "TelemetryService" = Depends(get_telemetry_service), + telemetry_service: TelemetryService = Depends(get_telemetry_service), ): """ Run a flow using a webhook request. @@ -484,7 +486,7 @@ async def process( tweaks: dict | None = None, clear_cache: Annotated[bool, Body(embed=True)] = False, # noqa: F821 session_id: Annotated[None | str, Body(embed=True)] = None, # noqa: F821 - task_service: "TaskService" = Depends(get_task_service), + task_service: TaskService = Depends(get_task_service), api_key_user: UserRead = Depends(api_key_security), sync: Annotated[bool, Body(embed=True)] = True, # noqa: F821 session_service: SessionService = Depends(get_session_service), @@ -633,7 +635,7 @@ def get_config(): try: from langflow.services.deps import get_settings_service - settings_service: "SettingsService" = get_settings_service() # type: ignore + settings_service: SettingsService = get_settings_service() # type: ignore return settings_service.settings.model_dump() except Exception as exc: logger.exception(exc) diff --git a/src/backend/base/langflow/base/agents/agent.py b/src/backend/base/langflow/base/agents/agent.py index 95959f7ed..0402f96b5 100644 --- a/src/backend/base/langflow/base/agents/agent.py +++ b/src/backend/base/langflow/base/agents/agent.py @@ -1,5 +1,5 @@ from abc import abstractmethod -from typing import List, Optional, Union, cast +from typing import cast from langchain.agents import AgentExecutor, BaseMultiActionAgent, BaseSingleActionAgent from langchain.agents.agent import RunnableAgent @@ -20,7 +20,7 @@ from langflow.utils.constants import MESSAGE_SENDER_AI class LCAgentComponent(Component): trace_type = "agent" - _base_inputs: List[InputTypes] = [ + _base_inputs: list[InputTypes] = [ MessageTextInput(name="input_value", display_name="Input"), BoolInput( name="handle_parsing_errors", @@ -89,7 +89,7 @@ class LCAgentComponent(Component): } return {**base, "agent_executor_kwargs": agent_kwargs} - def get_chat_history_data(self) -> Optional[List[Data]]: + def get_chat_history_data(self) -> list[Data] | None: # might be overridden in subclasses return None @@ -128,7 +128,7 @@ class LCToolsAgentComponent(LCAgentComponent): async def run_agent( self, - agent: Union[Runnable, BaseSingleActionAgent, BaseMultiActionAgent, AgentExecutor], + agent: Runnable | BaseSingleActionAgent | BaseMultiActionAgent | AgentExecutor, ) -> Text: if isinstance(agent, AgentExecutor): runnable = agent diff --git a/src/backend/base/langflow/base/agents/callback.py b/src/backend/base/langflow/base/agents/callback.py index 5d7260b6c..b1a1a3f33 100644 --- a/src/backend/base/langflow/base/agents/callback.py +++ b/src/backend/base/langflow/base/agents/callback.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List +from typing import Any from uuid import UUID from langchain.callbacks.base import AsyncCallbackHandler @@ -15,14 +15,14 @@ class AgentAsyncHandler(AsyncCallbackHandler): async def on_tool_start( self, - serialized: Dict[str, Any], + serialized: dict[str, Any], input_str: str, *, run_id: UUID, parent_run_id: UUID | None = None, - tags: List[str] | None = None, - metadata: Dict[str, Any] | None = None, - inputs: Dict[str, Any] | None = None, + tags: list[str] | None = None, + metadata: dict[str, Any] | None = None, + inputs: dict[str, Any] | None = None, **kwargs: Any, ) -> None: if self.log_function is None: @@ -62,7 +62,7 @@ class AgentAsyncHandler(AsyncCallbackHandler): *, run_id: UUID, parent_run_id: UUID | None = None, - tags: List[str] | None = None, + tags: list[str] | None = None, **kwargs: Any, ) -> None: if self.log_function is None: @@ -85,7 +85,7 @@ class AgentAsyncHandler(AsyncCallbackHandler): *, run_id: UUID, parent_run_id: UUID | None = None, - tags: List[str] | None = None, + tags: list[str] | None = None, **kwargs: Any, ) -> None: if self.log_function is None: diff --git a/src/backend/base/langflow/base/agents/crewai/crew.py b/src/backend/base/langflow/base/agents/crewai/crew.py index 359b87591..15c0a2d36 100644 --- a/src/backend/base/langflow/base/agents/crewai/crew.py +++ b/src/backend/base/langflow/base/agents/crewai/crew.py @@ -1,4 +1,5 @@ -from typing import Callable, List, Tuple, Union, cast +from collections.abc import Callable +from typing import cast from crewai import Agent, Crew, Process, Task # type: ignore from crewai.task import TaskOutput # type: ignore @@ -62,7 +63,7 @@ class BaseCrewComponent(Component): def get_step_callback( self, ) -> Callable: - def step_callback(agent_output: Union[AgentFinish, List[Tuple[AgentAction, str]]]): + def step_callback(agent_output: AgentFinish | list[tuple[AgentAction, str]]): _id = self._vertex.id if self._vertex else self.display_name if isinstance(agent_output, AgentFinish): messages = agent_output.messages diff --git a/src/backend/base/langflow/base/agents/utils.py b/src/backend/base/langflow/base/agents/utils.py index 2651ecb4a..ce48ef2d0 100644 --- a/src/backend/base/langflow/base/agents/utils.py +++ b/src/backend/base/langflow/base/agents/utils.py @@ -1,4 +1,5 @@ -from typing import Any, Callable, Dict, List, Optional, Sequence, Union +from collections.abc import Callable, Sequence +from typing import Any from langchain.agents import ( create_json_chat_agent, @@ -24,17 +25,17 @@ class AgentSpec(BaseModel): BaseLanguageModel, Sequence[BaseTool], BasePromptTemplate | ChatPromptTemplate, - Optional[Callable[[List[BaseTool]], str]], - Optional[Union[bool, List[str]]], + Callable[[list[BaseTool]], str] | None, + bool | list[str] | None, ], Any, ] - prompt: Optional[Any] = None - fields: List[str] - hub_repo: Optional[str] = None + prompt: Any | None = None + fields: list[str] + hub_repo: str | None = None -def data_to_messages(data: List[Data]) -> List[BaseMessage]: +def data_to_messages(data: list[Data]) -> list[BaseMessage]: """ Convert a list of data to a list of messages. @@ -51,9 +52,9 @@ def validate_and_create_xml_agent( llm: BaseLanguageModel, tools: Sequence[BaseTool], prompt: BasePromptTemplate, - tools_renderer: Callable[[List[BaseTool]], str] = render_text_description, + tools_renderer: Callable[[list[BaseTool]], str] = render_text_description, *, - stop_sequence: Union[bool, List[str]] = True, + stop_sequence: bool | list[str] = True, ): return create_xml_agent( llm=llm, @@ -68,9 +69,9 @@ def validate_and_create_openai_tools_agent( llm: BaseLanguageModel, tools: Sequence[BaseTool], prompt: ChatPromptTemplate, - tools_renderer: Callable[[List[BaseTool]], str] = render_text_description, + tools_renderer: Callable[[list[BaseTool]], str] = render_text_description, *, - stop_sequence: Union[bool, List[str]] = True, + stop_sequence: bool | list[str] = True, ): return create_openai_tools_agent( llm=llm, @@ -83,9 +84,9 @@ def validate_and_create_tool_calling_agent( llm: BaseLanguageModel, tools: Sequence[BaseTool], prompt: ChatPromptTemplate, - tools_renderer: Callable[[List[BaseTool]], str] = render_text_description, + tools_renderer: Callable[[list[BaseTool]], str] = render_text_description, *, - stop_sequence: Union[bool, List[str]] = True, + stop_sequence: bool | list[str] = True, ): return create_tool_calling_agent( llm=llm, @@ -98,9 +99,9 @@ def validate_and_create_json_chat_agent( llm: BaseLanguageModel, tools: Sequence[BaseTool], prompt: ChatPromptTemplate, - tools_renderer: Callable[[List[BaseTool]], str] = render_text_description, + tools_renderer: Callable[[list[BaseTool]], str] = render_text_description, *, - stop_sequence: Union[bool, List[str]] = True, + stop_sequence: bool | list[str] = True, ): return create_json_chat_agent( llm=llm, @@ -111,7 +112,7 @@ def validate_and_create_json_chat_agent( ) -AGENTS: Dict[str, AgentSpec] = { +AGENTS: dict[str, AgentSpec] = { "Tool Calling Agent": AgentSpec( func=validate_and_create_tool_calling_agent, prompt=None, diff --git a/src/backend/base/langflow/base/curl/parse.py b/src/backend/base/langflow/base/curl/parse.py index bb58e8fa2..245a2e26b 100644 --- a/src/backend/base/langflow/base/curl/parse.py +++ b/src/backend/base/langflow/base/curl/parse.py @@ -153,13 +153,13 @@ def parse_context(curl_command): # proxy_auth = parsed_args.proxy_user if parsed_args.proxy and parsed_args.proxy_user: proxies = { - "http": "http://{}@{}/".format(parsed_args.proxy_user, parsed_args.proxy), - "https": "http://{}@{}/".format(parsed_args.proxy_user, parsed_args.proxy), + "http": f"http://{parsed_args.proxy_user}@{parsed_args.proxy}/", + "https": f"http://{parsed_args.proxy_user}@{parsed_args.proxy}/", } elif parsed_args.proxy: proxies = { - "http": "http://{}/".format(parsed_args.proxy), - "https": "http://{}/".format(parsed_args.proxy), + "http": f"http://{parsed_args.proxy}/", + "https": f"http://{parsed_args.proxy}/", } return ParsedContext( diff --git a/src/backend/base/langflow/base/data/utils.py b/src/backend/base/langflow/base/data/utils.py index 7f8a41831..612bee050 100644 --- a/src/backend/base/langflow/base/data/utils.py +++ b/src/backend/base/langflow/base/data/utils.py @@ -1,8 +1,8 @@ import unicodedata import xml.etree.ElementTree as ET +from collections.abc import Callable from concurrent import futures from pathlib import Path -from typing import Callable, List, Optional import chardet import orjson @@ -49,8 +49,8 @@ def retrieve_file_paths( load_hidden: bool, recursive: bool, depth: int, - types: List[str] = TEXT_FILE_TYPES, -) -> List[str]: + types: list[str] = TEXT_FILE_TYPES, +) -> list[str]: path_obj = Path(path) if not path_obj.exists() or not path_obj.is_dir(): raise ValueError(f"Path {path} must exist and be a directory.") @@ -75,7 +75,7 @@ def retrieve_file_paths( return file_paths -def partition_file_to_data(file_path: str, silent_errors: bool) -> Optional[Data]: +def partition_file_to_data(file_path: str, silent_errors: bool) -> Data | None: # Use the partition function to load the file from unstructured.partition.auto import partition # type: ignore @@ -103,7 +103,7 @@ def read_text_file(file_path: str) -> str: if encoding in ["Windows-1252", "Windows-1254", "MacRoman"]: encoding = "utf-8" - with open(file_path, "r", encoding=encoding) as f: + with open(file_path, encoding=encoding) as f: return f.read() @@ -122,7 +122,7 @@ def parse_pdf_to_text(file_path: str) -> str: return "\n\n".join([page.extract_text() for page in reader.pages]) -def parse_text_file_to_data(file_path: str, silent_errors: bool) -> Optional[Data]: +def parse_text_file_to_data(file_path: str, silent_errors: bool) -> Data | None: try: if file_path.endswith(".pdf"): text = parse_pdf_to_text(file_path) @@ -171,11 +171,11 @@ def parse_text_file_to_data(file_path: str, silent_errors: bool) -> Optional[Dat def parallel_load_data( - file_paths: List[str], + file_paths: list[str], silent_errors: bool, max_concurrency: int, load_function: Callable = parse_text_file_to_data, -) -> List[Optional[Data]]: +) -> list[Data | None]: with futures.ThreadPoolExecutor(max_workers=max_concurrency) as executor: loaded_files = executor.map( lambda file_path: load_function(file_path, silent_errors), diff --git a/src/backend/base/langflow/base/flow_processing/utils.py b/src/backend/base/langflow/base/flow_processing/utils.py index 1505207ba..11bed4b86 100644 --- a/src/backend/base/langflow/base/flow_processing/utils.py +++ b/src/backend/base/langflow/base/flow_processing/utils.py @@ -1,5 +1,3 @@ -from typing import List - from loguru import logger from langflow.graph.schema import ResultData, RunOutputs @@ -7,7 +5,7 @@ from langflow.schema import Data from langflow.schema.message import Message -def build_data_from_run_outputs(run_outputs: RunOutputs) -> List[Data]: +def build_data_from_run_outputs(run_outputs: RunOutputs) -> list[Data]: """ Build a list of data from the given RunOutputs. @@ -27,7 +25,7 @@ def build_data_from_run_outputs(run_outputs: RunOutputs) -> List[Data]: return data -def build_data_from_result_data(result_data: ResultData, get_final_results_only: bool = True) -> List[Data]: +def build_data_from_result_data(result_data: ResultData, get_final_results_only: bool = True) -> list[Data]: """ Build a list of data from the given ResultData. @@ -80,7 +78,7 @@ def build_data_from_result_data(result_data: ResultData, get_final_results_only: return data -def format_flow_output_data(data: List[Data]) -> str: +def format_flow_output_data(data: list[Data]) -> str: """ Format the flow output data into a string. diff --git a/src/backend/base/langflow/base/io/chat.py b/src/backend/base/langflow/base/io/chat.py index e0390c91a..c4e4d7ee9 100644 --- a/src/backend/base/langflow/base/io/chat.py +++ b/src/backend/base/langflow/base/io/chat.py @@ -1,4 +1,4 @@ -from typing import AsyncIterator, Iterator, Optional, Union +from collections.abc import AsyncIterator, Iterator from langflow.custom import Component from langflow.memory import store_message @@ -54,7 +54,7 @@ class ChatComponent(Component): def _stream_message(self, message: Message, message_id: str) -> str: iterator = message.text - if not isinstance(iterator, (AsyncIterator, Iterator)): + if not isinstance(iterator, AsyncIterator | Iterator): raise ValueError("The message must be an iterator or an async iterator.") if isinstance(iterator, AsyncIterator): @@ -68,12 +68,12 @@ class ChatComponent(Component): def build_with_data( self, - sender: Optional[str] = "User", - sender_name: Optional[str] = "User", - input_value: Optional[Union[str, Data, Message]] = None, - files: Optional[list[str]] = None, - session_id: Optional[str] = None, - return_message: Optional[bool] = False, + sender: str | None = "User", + sender_name: str | None = "User", + input_value: str | Data | Message | None = None, + files: list[str] | None = None, + session_id: str | None = None, + return_message: bool | None = False, ) -> Message: message: Message | None = None diff --git a/src/backend/base/langflow/base/langchain_utilities/model.py b/src/backend/base/langflow/base/langchain_utilities/model.py index e2c1c0321..49772b270 100644 --- a/src/backend/base/langflow/base/langchain_utilities/model.py +++ b/src/backend/base/langflow/base/langchain_utilities/model.py @@ -1,5 +1,5 @@ from abc import abstractmethod -from typing import Sequence, Union +from collections.abc import Sequence from langflow.custom import Component from langflow.field_typing import Tool @@ -24,7 +24,7 @@ class LCToolComponent(Component): raise ValueError(f"Method '{method_name}' must be defined.") @abstractmethod - def run_model(self) -> Union[Data, list[Data]]: + def run_model(self) -> Data | list[Data]: """ Run model and return the output. """ diff --git a/src/backend/base/langflow/base/memory/memory.py b/src/backend/base/langflow/base/memory/memory.py index e55301a5f..24bc23815 100644 --- a/src/backend/base/langflow/base/memory/memory.py +++ b/src/backend/base/langflow/base/memory/memory.py @@ -1,5 +1,3 @@ -from typing import Optional - from langflow.custom import CustomComponent from langflow.schema import Data from langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_USER @@ -45,6 +43,6 @@ class BaseMemoryComponent(CustomComponent): raise NotImplementedError def add_message( - self, sender: str, sender_name: str, text: str, session_id: str, metadata: Optional[dict] = None, **kwargs + self, sender: str, sender_name: str, text: str, session_id: str, metadata: dict | None = None, **kwargs ): raise NotImplementedError diff --git a/src/backend/base/langflow/base/models/model.py b/src/backend/base/langflow/base/models/model.py index b32540ef6..22e976075 100644 --- a/src/backend/base/langflow/base/models/model.py +++ b/src/backend/base/langflow/base/models/model.py @@ -1,7 +1,6 @@ import json import warnings from abc import abstractmethod -from typing import List, Optional, Union from langchain_core.language_models.llms import LLM from langchain_core.messages import AIMessage, BaseMessage, HumanMessage, SystemMessage @@ -20,7 +19,7 @@ class LCModelComponent(Component): description: str = "Model Description" trace_type = "llm" - _base_inputs: List[InputTypes] = [ + _base_inputs: list[InputTypes] = [ MessageInput(name="input_value", display_name="Input"), MessageTextInput( name="system_message", @@ -138,9 +137,9 @@ class LCModelComponent(Component): runnable: LanguageModel, stream: bool, input_value: str | Message, - system_message: Optional[str] = None, + system_message: str | None = None, ): - messages: list[Union[BaseMessage]] = [] + messages: list[BaseMessage] = [] if not input_value and not system_message: raise ValueError("The message you want to send to the model is empty.") system_message_added = False @@ -161,7 +160,7 @@ class LCModelComponent(Component): if system_message and not system_message_added: messages.append(SystemMessage(content=system_message)) - inputs: Union[list, dict] = messages or {} + inputs: list | dict = messages or {} try: runnable = runnable.with_config( # type: ignore { diff --git a/src/backend/base/langflow/base/prompts/api_utils.py b/src/backend/base/langflow/base/prompts/api_utils.py index 93ddeeed4..0149c8e0e 100644 --- a/src/backend/base/langflow/base/prompts/api_utils.py +++ b/src/backend/base/langflow/base/prompts/api_utils.py @@ -1,5 +1,5 @@ from collections import defaultdict -from typing import Any, Dict, List, Optional +from typing import Any from fastapi import HTTPException from langchain_core.prompts import PromptTemplate @@ -198,7 +198,7 @@ def update_input_variables_field(input_variables, template): def process_prompt_template( - template: str, name: str, custom_fields: Optional[Dict[str, List[str]]], frontend_node_template: Dict[str, Any] + template: str, name: str, custom_fields: dict[str, list[str]] | None, frontend_node_template: dict[str, Any] ): """Process and validate prompt template, update template and custom fields.""" # Validate the prompt template and extract input variables diff --git a/src/backend/base/langflow/base/tools/flow_tool.py b/src/backend/base/langflow/base/tools/flow_tool.py index 421d7f728..52c1b2fae 100644 --- a/src/backend/base/langflow/base/tools/flow_tool.py +++ b/src/backend/base/langflow/base/tools/flow_tool.py @@ -1,5 +1,5 @@ import warnings -from typing import Any, List, Optional, Type +from typing import Any from langchain_core.runnables import RunnableConfig from langchain_core.tools import BaseTool, ToolException @@ -15,10 +15,10 @@ from langflow.utils.async_helpers import run_until_complete class FlowTool(BaseTool): name: str description: str - graph: Optional[Graph] = None - flow_id: Optional[str] = None - user_id: Optional[str] = None - inputs: List["Vertex"] = [] + graph: Graph | None = None + flow_id: str | None = None + user_id: str | None = None + inputs: list["Vertex"] = [] get_final_results_only: bool = True @property @@ -26,7 +26,7 @@ class FlowTool(BaseTool): schema = self.get_input_schema() return schema.schema()["properties"] - def get_input_schema(self, config: Optional[RunnableConfig] = None) -> Type[BaseModel]: + def get_input_schema(self, config: RunnableConfig | None = None) -> type[BaseModel]: """The tool's input schema.""" if self.args_schema is not None: return self.args_schema @@ -68,7 +68,7 @@ class FlowTool(BaseTool): data.extend(build_data_from_result_data(output, get_final_results_only=self.get_final_results_only)) return format_flow_output_data(data) - def validate_inputs(self, args_names: List[dict[str, str]], args: Any, kwargs: Any): + def validate_inputs(self, args_names: list[dict[str, str]], args: Any, kwargs: Any): """Validate the inputs.""" if len(args) > 0 and len(args) != len(args_names): diff --git a/src/backend/base/langflow/base/vectorstores/model.py b/src/backend/base/langflow/base/vectorstores/model.py index 415b3021d..ced43f88e 100644 --- a/src/backend/base/langflow/base/vectorstores/model.py +++ b/src/backend/base/langflow/base/vectorstores/model.py @@ -1,6 +1,6 @@ from abc import ABC, ABCMeta, abstractmethod from functools import wraps -from typing import List, cast +from typing import cast from langchain_core.documents import Document from loguru import logger @@ -94,7 +94,7 @@ class LCVectorStoreComponent(Component, ABC, metaclass=EnforceCacheDecoratorMeta vector_store: VectorStore, k=10, **kwargs, - ) -> List[Data]: + ) -> list[Data]: """ Search for data in the vector store based on the input value and search type. @@ -110,7 +110,7 @@ class LCVectorStoreComponent(Component, ABC, metaclass=EnforceCacheDecoratorMeta ValueError: If invalid inputs are provided. """ - docs: List[Document] = [] + docs: list[Document] = [] if input_value and isinstance(input_value, str) and hasattr(vector_store, "search"): docs = vector_store.search(query=input_value, search_type=search_type.lower(), k=k, **kwargs) else: @@ -140,7 +140,7 @@ class LCVectorStoreComponent(Component, ABC, metaclass=EnforceCacheDecoratorMeta else: raise ValueError(f"Vector Store {vector_store.__class__.__name__} does not have an as_retriever method.") - def search_documents(self) -> List[Data]: + def search_documents(self) -> list[Data]: """ Search for documents in the vector store. """ diff --git a/src/backend/base/langflow/components/Notion/add_content_to_page.py b/src/backend/base/langflow/components/Notion/add_content_to_page.py index 62fab006f..c88c10a6b 100644 --- a/src/backend/base/langflow/components/Notion/add_content_to_page.py +++ b/src/backend/base/langflow/components/Notion/add_content_to_page.py @@ -1,5 +1,5 @@ import json -from typing import Any, Dict, Union +from typing import Any import requests from bs4 import BeautifulSoup @@ -54,7 +54,7 @@ class AddContentToPage(LCToolComponent): args_schema=self.AddContentToPageSchema, ) - def _add_content_to_page(self, markdown_text: str, block_id: str) -> Union[Dict[str, Any], str]: + def _add_content_to_page(self, markdown_text: str, block_id: str) -> dict[str, Any] | str: try: html_text = markdown(markdown_text) soup = BeautifulSoup(html_text, "html.parser") @@ -209,7 +209,7 @@ class AddContentToPage(LCToolComponent): return blocks - def create_block(self, block_type: str, content: str, **kwargs) -> Dict[str, Any]: + def create_block(self, block_type: str, content: str, **kwargs) -> dict[str, Any]: block: dict[str, Any] = { "object": "block", "type": block_type, diff --git a/src/backend/base/langflow/components/Notion/create_page.py b/src/backend/base/langflow/components/Notion/create_page.py index 2c369a9c1..5c832c286 100644 --- a/src/backend/base/langflow/components/Notion/create_page.py +++ b/src/backend/base/langflow/components/Notion/create_page.py @@ -1,5 +1,5 @@ import json -from typing import Any, Dict, Union +from typing import Any import requests from langchain.tools import StructuredTool @@ -60,7 +60,7 @@ class NotionPageCreator(LCToolComponent): args_schema=self.NotionPageCreatorSchema, ) - def _create_notion_page(self, database_id: str, properties_json: str) -> Union[Dict[str, Any], str]: + def _create_notion_page(self, database_id: str, properties_json: str) -> dict[str, Any] | str: if not database_id or not properties_json: return "Invalid input. Please provide 'database_id' and 'properties_json'." diff --git a/src/backend/base/langflow/components/Notion/list_database_properties.py b/src/backend/base/langflow/components/Notion/list_database_properties.py index f45517ac0..e615f58f7 100644 --- a/src/backend/base/langflow/components/Notion/list_database_properties.py +++ b/src/backend/base/langflow/components/Notion/list_database_properties.py @@ -1,5 +1,3 @@ -from typing import Dict, Union - import requests from langchain.tools import StructuredTool from pydantic import BaseModel, Field @@ -50,7 +48,7 @@ class NotionDatabaseProperties(LCToolComponent): args_schema=self.NotionDatabasePropertiesSchema, ) - def _fetch_database_properties(self, database_id: str) -> Union[Dict, str]: + def _fetch_database_properties(self, database_id: str) -> dict | str: url = f"https://api.notion.com/v1/databases/{database_id}" headers = { "Authorization": f"Bearer {self.notion_secret}", diff --git a/src/backend/base/langflow/components/Notion/list_pages.py b/src/backend/base/langflow/components/Notion/list_pages.py index be6d9ad9d..e71f155d5 100644 --- a/src/backend/base/langflow/components/Notion/list_pages.py +++ b/src/backend/base/langflow/components/Notion/list_pages.py @@ -1,5 +1,5 @@ import json -from typing import Any, Dict, List, Optional +from typing import Any import requests from langchain.tools import StructuredTool @@ -43,12 +43,12 @@ class NotionListPages(LCToolComponent): class NotionListPagesSchema(BaseModel): database_id: str = Field(..., description="The ID of the Notion database to query.") - query_json: Optional[str] = Field( + query_json: str | None = Field( default="", description="A JSON string containing the filters and sorts for querying the database. Leave empty for no filters or sorts.", ) - def run_model(self) -> List[Data]: + def run_model(self) -> list[Data]: result = self._query_notion_database(self.database_id, self.query_json) if isinstance(result, str): @@ -89,7 +89,7 @@ class NotionListPages(LCToolComponent): args_schema=self.NotionListPagesSchema, ) - def _query_notion_database(self, database_id: str, query_json: Optional[str] = None) -> List[Dict[str, Any]] | str: + def _query_notion_database(self, database_id: str, query_json: str | None = None) -> list[dict[str, Any]] | str: url = f"https://api.notion.com/v1/databases/{database_id}/query" headers = { "Authorization": f"Bearer {self.notion_secret}", diff --git a/src/backend/base/langflow/components/Notion/list_users.py b/src/backend/base/langflow/components/Notion/list_users.py index 865e84639..fd2fa0c6d 100644 --- a/src/backend/base/langflow/components/Notion/list_users.py +++ b/src/backend/base/langflow/components/Notion/list_users.py @@ -1,5 +1,3 @@ -from typing import Dict, List - import requests from langchain.tools import StructuredTool from pydantic import BaseModel @@ -28,7 +26,7 @@ class NotionUserList(LCToolComponent): class NotionUserListSchema(BaseModel): pass - def run_model(self) -> List[Data]: + def run_model(self) -> list[Data]: users = self._list_users() records = [] combined_text = "" @@ -53,7 +51,7 @@ class NotionUserList(LCToolComponent): args_schema=self.NotionUserListSchema, ) - def _list_users(self) -> List[Dict]: + def _list_users(self) -> list[dict]: url = "https://api.notion.com/v1/users" headers = { "Authorization": f"Bearer {self.notion_secret}", diff --git a/src/backend/base/langflow/components/Notion/search.py b/src/backend/base/langflow/components/Notion/search.py index 9c9173bdf..1f1a7069e 100644 --- a/src/backend/base/langflow/components/Notion/search.py +++ b/src/backend/base/langflow/components/Notion/search.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List +from typing import Any import requests from langchain.tools import StructuredTool @@ -49,7 +49,7 @@ class NotionSearch(LCToolComponent): filter_value: str = Field(default="page", description="Filter type: 'page' or 'database'.") sort_direction: str = Field(default="descending", description="Sort direction: 'ascending' or 'descending'.") - def run_model(self) -> List[Data]: + def run_model(self) -> list[Data]: results = self._search_notion(self.query, self.filter_value, self.sort_direction) records = [] combined_text = f"Results found: {len(results)}\n\n" @@ -89,7 +89,7 @@ class NotionSearch(LCToolComponent): def _search_notion( self, query: str, filter_value: str = "page", sort_direction: str = "descending" - ) -> List[Dict[str, Any]]: + ) -> list[dict[str, Any]]: url = "https://api.notion.com/v1/search" headers = { "Authorization": f"Bearer {self.notion_secret}", diff --git a/src/backend/base/langflow/components/Notion/update_page_property.py b/src/backend/base/langflow/components/Notion/update_page_property.py index 9f24dfe45..1f0a54f3b 100644 --- a/src/backend/base/langflow/components/Notion/update_page_property.py +++ b/src/backend/base/langflow/components/Notion/update_page_property.py @@ -1,5 +1,5 @@ import json -from typing import Any, Dict, Union +from typing import Any import requests from langchain.tools import StructuredTool @@ -39,7 +39,7 @@ class NotionPageUpdate(LCToolComponent): class NotionPageUpdateSchema(BaseModel): page_id: str = Field(..., description="The ID of the Notion page to update.") - properties: Union[str, Dict[str, Any]] = Field( + properties: str | dict[str, Any] = Field( ..., description="The properties to update on the page (as a JSON string or a dictionary)." ) @@ -63,7 +63,7 @@ class NotionPageUpdate(LCToolComponent): args_schema=self.NotionPageUpdateSchema, ) - def _update_notion_page(self, page_id: str, properties: Union[str, Dict[str, Any]]) -> Union[Dict[str, Any], str]: + def _update_notion_page(self, page_id: str, properties: str | dict[str, Any]) -> dict[str, Any] | str: url = f"https://api.notion.com/v1/pages/{page_id}" headers = { "Authorization": f"Bearer {self.notion_secret}", diff --git a/src/backend/base/langflow/components/agents/JsonAgent.py b/src/backend/base/langflow/components/agents/JsonAgent.py index 4263716c8..38010d871 100644 --- a/src/backend/base/langflow/components/agents/JsonAgent.py +++ b/src/backend/base/langflow/components/agents/JsonAgent.py @@ -22,7 +22,7 @@ class JsonAgentComponent(LCAgentComponent): def build_agent(self) -> AgentExecutor: if self.path.endswith("yaml") or self.path.endswith("yml"): - with open(self.path, "r") as file: + with open(self.path) as file: yaml_dict = yaml.load(file, Loader=yaml.FullLoader) spec = JsonSpec(dict_=yaml_dict) else: diff --git a/src/backend/base/langflow/components/agents/OpenAIToolsAgent.py b/src/backend/base/langflow/components/agents/OpenAIToolsAgent.py index ef614f877..80e3d036b 100644 --- a/src/backend/base/langflow/components/agents/OpenAIToolsAgent.py +++ b/src/backend/base/langflow/components/agents/OpenAIToolsAgent.py @@ -1,5 +1,3 @@ -from typing import List, Optional - from langchain.agents import create_openai_tools_agent from langchain_core.prompts import ChatPromptTemplate, HumanMessagePromptTemplate, PromptTemplate @@ -35,7 +33,7 @@ class OpenAIToolsAgentComponent(LCToolsAgentComponent): DataInput(name="chat_history", display_name="Chat History", is_list=True, advanced=True), ] - def get_chat_history_data(self) -> Optional[List[Data]]: + def get_chat_history_data(self) -> list[Data] | None: return self.chat_history def create_agent_runnable(self): diff --git a/src/backend/base/langflow/components/agents/OpenAPIAgent.py b/src/backend/base/langflow/components/agents/OpenAPIAgent.py index 3123cd72e..50c76c423 100644 --- a/src/backend/base/langflow/components/agents/OpenAPIAgent.py +++ b/src/backend/base/langflow/components/agents/OpenAPIAgent.py @@ -24,7 +24,7 @@ class OpenAPIAgentComponent(LCAgentComponent): def build_agent(self) -> AgentExecutor: if self.path.endswith("yaml") or self.path.endswith("yml"): - with open(self.path, "r") as file: + with open(self.path) as file: yaml_dict = yaml.load(file, Loader=yaml.FullLoader) spec = JsonSpec(dict_=yaml_dict) else: diff --git a/src/backend/base/langflow/components/agents/ToolCallingAgent.py b/src/backend/base/langflow/components/agents/ToolCallingAgent.py index 0b945a176..af63f2f82 100644 --- a/src/backend/base/langflow/components/agents/ToolCallingAgent.py +++ b/src/backend/base/langflow/components/agents/ToolCallingAgent.py @@ -1,5 +1,3 @@ -from typing import List, Optional - from langchain.agents import create_tool_calling_agent from langchain_core.prompts import ChatPromptTemplate, HumanMessagePromptTemplate, PromptTemplate @@ -30,7 +28,7 @@ class ToolCallingAgentComponent(LCToolsAgentComponent): DataInput(name="chat_history", display_name="Chat History", is_list=True, advanced=True), ] - def get_chat_history_data(self) -> Optional[List[Data]]: + def get_chat_history_data(self) -> list[Data] | None: return self.chat_history def create_agent_runnable(self): diff --git a/src/backend/base/langflow/components/agents/XMLAgent.py b/src/backend/base/langflow/components/agents/XMLAgent.py index 60d532dfc..5044bb0dc 100644 --- a/src/backend/base/langflow/components/agents/XMLAgent.py +++ b/src/backend/base/langflow/components/agents/XMLAgent.py @@ -1,5 +1,3 @@ -from typing import List, Optional - from langchain.agents import create_xml_agent from langchain_core.prompts import ChatPromptTemplate, HumanMessagePromptTemplate, PromptTemplate @@ -52,7 +50,7 @@ Question: {input} ), ] - def get_chat_history_data(self) -> Optional[List[Data]]: + def get_chat_history_data(self) -> list[Data] | None: return self.chat_history def create_agent_runnable(self): diff --git a/src/backend/base/langflow/components/astra_assistants/run.py b/src/backend/base/langflow/components/astra_assistants/run.py index 28b48c615..506547f89 100644 --- a/src/backend/base/langflow/components/astra_assistants/run.py +++ b/src/backend/base/langflow/components/astra_assistants/run.py @@ -1,4 +1,4 @@ -from typing import Any, Optional +from typing import Any from astra_assistants import patch # type: ignore from openai import OpenAI @@ -20,7 +20,7 @@ class AssistantsRun(Component): self, build_config: dotdict, field_value: Any, - field_name: Optional[str] = None, + field_name: str | None = None, ): if field_name == "thread_id": if field_value is None: diff --git a/src/backend/base/langflow/components/data/APIRequest.py b/src/backend/base/langflow/components/data/APIRequest.py index 8a1656a58..70e26a61f 100644 --- a/src/backend/base/langflow/components/data/APIRequest.py +++ b/src/backend/base/langflow/components/data/APIRequest.py @@ -1,6 +1,6 @@ import asyncio import json -from typing import Any, List, Optional +from typing import Any from urllib.parse import parse_qsl, urlencode, urlparse, urlunparse import httpx @@ -104,8 +104,8 @@ class APIRequestComponent(Component): client: httpx.AsyncClient, method: str, url: str, - headers: Optional[dict] = None, - body: Optional[dict] = None, + headers: dict | None = None, + body: dict | None = None, timeout: int = 5, ) -> Data: method = method.upper() @@ -162,7 +162,7 @@ class APIRequestComponent(Component): url_parts[4] = urlencode(query) return urlunparse(url_parts) - async def make_requests(self) -> List[Data]: + async def make_requests(self) -> list[Data]: method = self.method urls = [url.strip() for url in self.urls if url.strip()] curl = self.curl diff --git a/src/backend/base/langflow/components/data/Directory.py b/src/backend/base/langflow/components/data/Directory.py index 55a515f51..cf912da60 100644 --- a/src/backend/base/langflow/components/data/Directory.py +++ b/src/backend/base/langflow/components/data/Directory.py @@ -1,5 +1,3 @@ -from typing import List - from langflow.base.data.utils import parallel_load_data, parse_text_file_to_data, retrieve_file_paths from langflow.custom import Component from langflow.io import BoolInput, IntInput, MessageTextInput @@ -68,7 +66,7 @@ class DirectoryComponent(Component): Output(display_name="Data", name="data", method="load_directory"), ] - def load_directory(self) -> List[Data]: + def load_directory(self) -> list[Data]: path = self.path types = self.types or [] # self.types is already a list due to is_list=True depth = self.depth diff --git a/src/backend/base/langflow/components/data/Gmail.py b/src/backend/base/langflow/components/data/Gmail.py index 3b4b93006..6c450638b 100644 --- a/src/backend/base/langflow/components/data/Gmail.py +++ b/src/backend/base/langflow/components/data/Gmail.py @@ -1,8 +1,9 @@ import base64 import json import re +from collections.abc import Iterator from json.decoder import JSONDecodeError -from typing import Any, Iterator, List, Optional +from typing import Any from google.auth.exceptions import RefreshError from google.oauth2.credentials import Credentials @@ -29,7 +30,7 @@ class GmailLoaderComponent(Component): display_name="JSON String of the Service Account Token", info="JSON string containing OAuth 2.0 access token information for service account access", required=True, - value=str("""{ + value="""{ "account": "", "client_id": "", "client_secret": "", @@ -41,7 +42,7 @@ class GmailLoaderComponent(Component): "token": "", "token_uri": "https://oauth2.googleapis.com/token", "universe_domain": "googleapis.com" - }"""), + }""", ), MessageTextInput( name="label_ids", @@ -66,7 +67,7 @@ class GmailLoaderComponent(Component): def load_emails(self) -> Data: class CustomGMailLoader(GMailLoader): def __init__( - self, creds: Any, n: int = 100, label_ids: Optional[List[str]] = None, raise_error: bool = False + self, creds: Any, n: int = 100, label_ids: list[str] | None = None, raise_error: bool = False ) -> None: super().__init__(creds, n, raise_error) self.label_ids = label_ids if label_ids is not None else ["SENT"] diff --git a/src/backend/base/langflow/components/data/GoogleDrive.py b/src/backend/base/langflow/components/data/GoogleDrive.py index 5e82202ec..131f0dc29 100644 --- a/src/backend/base/langflow/components/data/GoogleDrive.py +++ b/src/backend/base/langflow/components/data/GoogleDrive.py @@ -1,6 +1,5 @@ import json from json.decoder import JSONDecodeError -from typing import Optional from google.auth.exceptions import RefreshError from google.oauth2.credentials import Credentials @@ -37,7 +36,7 @@ class GoogleDriveComponent(Component): def load_documents(self) -> Data: class CustomGoogleDriveLoader(GoogleDriveLoader): - creds: Optional[Credentials] = None + creds: Credentials | None = None """Credentials object to be passed directly.""" def _load_credentials(self): diff --git a/src/backend/base/langflow/components/data/GoogleDriveSearch.py b/src/backend/base/langflow/components/data/GoogleDriveSearch.py index 1c9b2b6e5..4e34a58cf 100644 --- a/src/backend/base/langflow/components/data/GoogleDriveSearch.py +++ b/src/backend/base/langflow/components/data/GoogleDriveSearch.py @@ -1,5 +1,4 @@ import json -from typing import List from google.oauth2.credentials import Credentials from googleapiclient.discovery import build @@ -146,13 +145,13 @@ class GoogleDriveSearchComponent(Component): return {"doc_urls": doc_urls, "doc_ids": doc_ids, "doc_titles_urls": doc_titles_urls, "doc_titles": doc_titles} - def search_doc_ids(self) -> List[str]: + def search_doc_ids(self) -> list[str]: return self.search_files()["doc_ids"] - def search_doc_urls(self) -> List[str]: + def search_doc_urls(self) -> list[str]: return self.search_files()["doc_urls"] - def search_doc_titles(self) -> List[str]: + def search_doc_titles(self) -> list[str]: return self.search_files()["doc_titles"] def search_data(self) -> Data: diff --git a/src/backend/base/langflow/components/deactivated/DocumentsToData.py b/src/backend/base/langflow/components/deactivated/DocumentsToData.py index 3d0f65970..5eaf6b60e 100644 --- a/src/backend/base/langflow/components/deactivated/DocumentsToData.py +++ b/src/backend/base/langflow/components/deactivated/DocumentsToData.py @@ -1,5 +1,3 @@ -from typing import List - from langchain_core.documents import Document from langflow.custom import CustomComponent @@ -16,7 +14,7 @@ class DocumentsToDataComponent(CustomComponent): "documents": {"display_name": "Documents"}, } - def build(self, documents: List[Document]) -> List[Data]: + def build(self, documents: list[Document]) -> list[Data]: if isinstance(documents, Document): documents = [documents] data = [Data.from_document(document) for document in documents] diff --git a/src/backend/base/langflow/components/deactivated/ListFlows.py b/src/backend/base/langflow/components/deactivated/ListFlows.py index fa46839a2..5e18e9cfa 100644 --- a/src/backend/base/langflow/components/deactivated/ListFlows.py +++ b/src/backend/base/langflow/components/deactivated/ListFlows.py @@ -1,5 +1,3 @@ -from typing import List - from langflow.custom import CustomComponent from langflow.schema import Data @@ -16,7 +14,7 @@ class ListFlowsComponent(CustomComponent): def build( self, - ) -> List[Data]: + ) -> list[Data]: flows = self.list_flows() self.status = flows return flows diff --git a/src/backend/base/langflow/components/deactivated/Message.py b/src/backend/base/langflow/components/deactivated/Message.py index ae9313475..090990713 100644 --- a/src/backend/base/langflow/components/deactivated/Message.py +++ b/src/backend/base/langflow/components/deactivated/Message.py @@ -1,5 +1,3 @@ -from typing import Optional - from langflow.custom import CustomComponent from langflow.schema.message import Message from langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_USER @@ -28,8 +26,8 @@ class MessageComponent(CustomComponent): def build( self, sender: str = MESSAGE_SENDER_USER, - sender_name: Optional[str] = None, - session_id: Optional[str] = None, + sender_name: str | None = None, + session_id: str | None = None, text: str = "", ) -> Message: message = Message( diff --git a/src/backend/base/langflow/components/deactivated/SplitText.py b/src/backend/base/langflow/components/deactivated/SplitText.py index 88f911057..b83aaa0d4 100644 --- a/src/backend/base/langflow/components/deactivated/SplitText.py +++ b/src/backend/base/langflow/components/deactivated/SplitText.py @@ -1,5 +1,3 @@ -from typing import List - from langchain_text_splitters import CharacterTextSplitter from langflow.custom import Component @@ -52,7 +50,7 @@ class SplitTextComponent(Component): data.append(Data(text=doc.page_content, data=doc.metadata)) return data - def split_text(self) -> List[Data]: + def split_text(self) -> list[Data]: separator = unescape_string(self.separator) documents = [] diff --git a/src/backend/base/langflow/components/deactivated/SubFlow.py b/src/backend/base/langflow/components/deactivated/SubFlow.py index ea81a5727..763840deb 100644 --- a/src/backend/base/langflow/components/deactivated/SubFlow.py +++ b/src/backend/base/langflow/components/deactivated/SubFlow.py @@ -1,4 +1,4 @@ -from typing import Any, List, Optional +from typing import Any from loguru import logger @@ -22,11 +22,11 @@ class SubFlowComponent(CustomComponent): field_order = ["flow_name"] name = "SubFlow" - def get_flow_names(self) -> List[str]: + def get_flow_names(self) -> list[str]: flow_datas = self.list_flows() return [flow_data.data["name"] for flow_data in flow_datas] - def get_flow(self, flow_name: str) -> Optional[Data]: + def get_flow(self, flow_name: str) -> Data | None: flow_datas = self.list_flows() for flow_data in flow_datas: if flow_data.data["name"] == flow_name: @@ -56,7 +56,7 @@ class SubFlowComponent(CustomComponent): return build_config - def add_inputs_to_build_config(self, inputs: List[Vertex], build_config: dotdict): + def add_inputs_to_build_config(self, inputs: list[Vertex], build_config: dotdict): new_fields: list[Input] = [] for vertex in inputs: field = Input( @@ -96,9 +96,9 @@ class SubFlowComponent(CustomComponent): }, } - async def build(self, flow_name: str, get_final_results_only: bool = True, **kwargs) -> List[Data]: + async def build(self, flow_name: str, get_final_results_only: bool = True, **kwargs) -> list[Data]: tweaks = {key: {"input_value": value} for key, value in kwargs.items()} - run_outputs: List[Optional[RunOutputs]] = await self.run_flow( + run_outputs: list[RunOutputs | None] = await self.run_flow( tweaks=tweaks, flow_name=flow_name, ) diff --git a/src/backend/base/langflow/components/documentloaders/Confluence.py b/src/backend/base/langflow/components/documentloaders/Confluence.py index af64b5529..a24d44676 100644 --- a/src/backend/base/langflow/components/documentloaders/Confluence.py +++ b/src/backend/base/langflow/components/documentloaders/Confluence.py @@ -1,5 +1,3 @@ -from typing import List - from langchain_community.document_loaders import ConfluenceLoader from langchain_community.document_loaders.confluence import ContentFormat @@ -79,7 +77,7 @@ class ConfluenceComponent(Component): ) return loader - def load_documents(self) -> List[Data]: + def load_documents(self) -> list[Data]: confluence = self.build_confluence() documents = confluence.load() data = [Data.from_document(doc) for doc in documents] # Using the from_document method of Data diff --git a/src/backend/base/langflow/components/documentloaders/GitLoader.py b/src/backend/base/langflow/components/documentloaders/GitLoader.py index a349c9d3e..1218b1b97 100644 --- a/src/backend/base/langflow/components/documentloaders/GitLoader.py +++ b/src/backend/base/langflow/components/documentloaders/GitLoader.py @@ -1,6 +1,5 @@ import re from pathlib import Path -from typing import List from langchain_community.document_loaders.git import GitLoader @@ -109,7 +108,7 @@ class GitLoaderComponent(Component): ) return loader - def load_documents(self) -> List[Data]: + def load_documents(self) -> list[Data]: gitloader = self.build_gitloader() documents = list(gitloader.lazy_load()) data = [Data.from_document(doc) for doc in documents] diff --git a/src/backend/base/langflow/components/documentloaders/Unstructured.py b/src/backend/base/langflow/components/documentloaders/Unstructured.py index 937d58aeb..02081e0f1 100644 --- a/src/backend/base/langflow/components/documentloaders/Unstructured.py +++ b/src/backend/base/langflow/components/documentloaders/Unstructured.py @@ -1,5 +1,3 @@ -from typing import List - from langchain_unstructured import UnstructuredLoader from langflow.custom import Component @@ -47,7 +45,7 @@ class UnstructuredComponent(Component): return loader - def load_documents(self) -> List[Data]: + def load_documents(self) -> list[Data]: unstructured = self.build_unstructured() documents = unstructured.load() diff --git a/src/backend/base/langflow/components/embeddings/EmbeddingSimilarity.py b/src/backend/base/langflow/components/embeddings/EmbeddingSimilarity.py index 79493683b..b244be8e2 100644 --- a/src/backend/base/langflow/components/embeddings/EmbeddingSimilarity.py +++ b/src/backend/base/langflow/components/embeddings/EmbeddingSimilarity.py @@ -1,5 +1,3 @@ -from typing import List - import numpy as np from langflow.custom import Component @@ -33,7 +31,7 @@ class EmbeddingSimilarityComponent(Component): ] def compute_similarity(self) -> Data: - embedding_vectors: List[Data] = self.embedding_vectors + embedding_vectors: list[Data] = self.embedding_vectors # Assert that the list contains exactly two Data objects assert len(embedding_vectors) == 2, "Exactly two embedding vectors are required." diff --git a/src/backend/base/langflow/components/embeddings/GoogleGenerativeAIEmbeddings.py b/src/backend/base/langflow/components/embeddings/GoogleGenerativeAIEmbeddings.py index 128b8c4f8..dd6922b20 100644 --- a/src/backend/base/langflow/components/embeddings/GoogleGenerativeAIEmbeddings.py +++ b/src/backend/base/langflow/components/embeddings/GoogleGenerativeAIEmbeddings.py @@ -1,17 +1,11 @@ # from langflow.field_typing import Data -from typing import List, Optional - import numpy as np # TODO: remove ignore once the google package is published with types -from google.ai.generativelanguage_v1beta.types import ( - BatchEmbedContentsRequest, -) +from google.ai.generativelanguage_v1beta.types import BatchEmbedContentsRequest from langchain_core.embeddings import Embeddings from langchain_google_genai import GoogleGenerativeAIEmbeddings -from langchain_google_genai._common import ( - GoogleGenerativeAIError, -) +from langchain_google_genai._common import GoogleGenerativeAIError from langflow.custom import Component from langflow.io import MessageTextInput, Output, SecretStrInput @@ -43,13 +37,13 @@ class GoogleGenerativeAIEmbeddingsComponent(Component): def embed_documents( self, - texts: List[str], + texts: list[str], *, batch_size: int = 100, - task_type: Optional[str] = None, - titles: Optional[List[str]] = None, - output_dimensionality: Optional[int] = 1536, - ) -> List[List[float]]: + task_type: str | None = None, + titles: list[str] | None = None, + output_dimensionality: int | None = 1536, + ) -> list[list[float]]: """Embed a list of strings. Google Generative AI currently sets a max batch size of 100 strings. @@ -64,7 +58,7 @@ class GoogleGenerativeAIEmbeddingsComponent(Component): Returns: List of embeddings, one for each text. """ - embeddings: List[List[float]] = [] + embeddings: list[list[float]] = [] batch_start_index = 0 for batch in GoogleGenerativeAIEmbeddings._prepare_batches(texts, batch_size): if titles: @@ -95,10 +89,10 @@ class GoogleGenerativeAIEmbeddingsComponent(Component): def embed_query( self, text: str, - task_type: Optional[str] = None, - title: Optional[str] = None, - output_dimensionality: Optional[int] = 1536, - ) -> List[float]: + task_type: str | None = None, + title: str | None = None, + output_dimensionality: int | None = 1536, + ) -> list[float]: """Embed a text. Args: diff --git a/src/backend/base/langflow/components/embeddings/util/AIMLEmbeddingsImpl.py b/src/backend/base/langflow/components/embeddings/util/AIMLEmbeddingsImpl.py index 34859dd81..ef77c2835 100644 --- a/src/backend/base/langflow/components/embeddings/util/AIMLEmbeddingsImpl.py +++ b/src/backend/base/langflow/components/embeddings/util/AIMLEmbeddingsImpl.py @@ -1,6 +1,5 @@ import concurrent.futures import json -from typing import List import httpx from langchain_core.pydantic_v1 import BaseModel, SecretStr @@ -15,7 +14,7 @@ class AIMLEmbeddingsImpl(BaseModel, Embeddings): api_key: SecretStr model: str - def embed_documents(self, texts: List[str]) -> List[List[float]]: + def embed_documents(self, texts: list[str]) -> list[list[float]]: embeddings = [None] * len(texts) headers = { "Content-Type": "application/json", @@ -58,5 +57,5 @@ class AIMLEmbeddingsImpl(BaseModel, Embeddings): result_data = response.json() return result_data - def embed_query(self, text: str) -> List[float]: + def embed_query(self, text: str) -> list[float]: return self.embed_documents([text])[0] diff --git a/src/backend/base/langflow/components/helpers/FilterData.py b/src/backend/base/langflow/components/helpers/FilterData.py index 3832e4e84..bbdfe681c 100644 --- a/src/backend/base/langflow/components/helpers/FilterData.py +++ b/src/backend/base/langflow/components/helpers/FilterData.py @@ -1,5 +1,3 @@ -from typing import List - from langflow.custom import Component from langflow.io import DataInput, MessageTextInput, Output from langflow.schema import Data @@ -31,7 +29,7 @@ class FilterDataComponent(Component): ] def filter_data(self) -> Data: - filter_criteria: List[str] = self.filter_criteria + filter_criteria: list[str] = self.filter_criteria data = self.data.data if isinstance(self.data, Data) else {} # Filter the data diff --git a/src/backend/base/langflow/components/helpers/IDGenerator.py b/src/backend/base/langflow/components/helpers/IDGenerator.py index c7051d129..348f2bced 100644 --- a/src/backend/base/langflow/components/helpers/IDGenerator.py +++ b/src/backend/base/langflow/components/helpers/IDGenerator.py @@ -1,5 +1,5 @@ import uuid -from typing import Any, Optional +from typing import Any from langflow.custom import Component from langflow.io import MessageTextInput, Output @@ -26,7 +26,7 @@ class IDGeneratorComponent(Component): Output(display_name="ID", name="id", method="generate_id"), ] - def update_build_config(self, build_config: dotdict, field_value: Any, field_name: Optional[str] = None): + def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None): if field_name == "unique_id": build_config[field_name]["value"] = str(uuid.uuid4()) return build_config diff --git a/src/backend/base/langflow/components/helpers/SplitText.py b/src/backend/base/langflow/components/helpers/SplitText.py index 88f911057..b83aaa0d4 100644 --- a/src/backend/base/langflow/components/helpers/SplitText.py +++ b/src/backend/base/langflow/components/helpers/SplitText.py @@ -1,5 +1,3 @@ -from typing import List - from langchain_text_splitters import CharacterTextSplitter from langflow.custom import Component @@ -52,7 +50,7 @@ class SplitTextComponent(Component): data.append(Data(text=doc.page_content, data=doc.metadata)) return data - def split_text(self) -> List[Data]: + def split_text(self) -> list[Data]: separator = unescape_string(self.separator) documents = [] diff --git a/src/backend/base/langflow/components/langchain_utilities/FirecrawlCrawlApi.py b/src/backend/base/langflow/components/langchain_utilities/FirecrawlCrawlApi.py index e152b797e..0db626bf6 100644 --- a/src/backend/base/langflow/components/langchain_utilities/FirecrawlCrawlApi.py +++ b/src/backend/base/langflow/components/langchain_utilities/FirecrawlCrawlApi.py @@ -1,5 +1,4 @@ import uuid -from typing import Optional from langflow.custom import CustomComponent from langflow.schema import Data @@ -51,9 +50,9 @@ class FirecrawlCrawlApi(CustomComponent): api_key: str, url: str, timeout: int = 30000, - crawlerOptions: Optional[Data] = None, - pageOptions: Optional[Data] = None, - idempotency_key: Optional[str] = None, + crawlerOptions: Data | None = None, + pageOptions: Data | None = None, + idempotency_key: str | None = None, ) -> Data: try: from firecrawl.firecrawl import FirecrawlApp # type: ignore diff --git a/src/backend/base/langflow/components/langchain_utilities/FirecrawlScrapeApi.py b/src/backend/base/langflow/components/langchain_utilities/FirecrawlScrapeApi.py index 9ed720a67..76f823070 100644 --- a/src/backend/base/langflow/components/langchain_utilities/FirecrawlScrapeApi.py +++ b/src/backend/base/langflow/components/langchain_utilities/FirecrawlScrapeApi.py @@ -1,5 +1,3 @@ -from typing import Optional - from langflow.custom import CustomComponent from langflow.schema import Data @@ -46,8 +44,8 @@ class FirecrawlScrapeApi(CustomComponent): api_key: str, url: str, timeout: int = 10000, - pageOptions: Optional[Data] = None, - extractorOptions: Optional[Data] = None, + pageOptions: Data | None = None, + extractorOptions: Data | None = None, ) -> Data: try: from firecrawl.firecrawl import FirecrawlApp # type: ignore diff --git a/src/backend/base/langflow/components/models/GroqModel.py b/src/backend/base/langflow/components/models/GroqModel.py index 4351e17e8..f6250a5fd 100644 --- a/src/backend/base/langflow/components/models/GroqModel.py +++ b/src/backend/base/langflow/components/models/GroqModel.py @@ -1,5 +1,3 @@ -from typing import List - import requests from langchain_groq import ChatGroq from pydantic.v1 import SecretStr @@ -55,7 +53,7 @@ class GroqModel(LCModelComponent): ), ] - def get_models(self) -> List[str]: + def get_models(self) -> list[str]: api_key = self.groq_api_key base_url = self.groq_api_base or "https://api.groq.com" url = f"{base_url}/openai/v1/models" diff --git a/src/backend/base/langflow/components/models/HuggingFaceModel.py b/src/backend/base/langflow/components/models/HuggingFaceModel.py index c627d0075..bed126790 100644 --- a/src/backend/base/langflow/components/models/HuggingFaceModel.py +++ b/src/backend/base/langflow/components/models/HuggingFaceModel.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Optional +from typing import Any from langchain_community.llms.huggingface_endpoint import HuggingFaceEndpoint from tenacity import retry, stop_after_attempt, wait_fixed @@ -84,15 +84,15 @@ class HuggingFaceEndpointsComponent(LCModelComponent): def create_huggingface_endpoint( self, model_id: str, - task: Optional[str], - huggingfacehub_api_token: Optional[str], - model_kwargs: Dict[str, Any], + task: str | None, + huggingfacehub_api_token: str | None, + model_kwargs: dict[str, Any], max_new_tokens: int, - top_k: Optional[int], + top_k: int | None, top_p: float, - typical_p: Optional[float], - temperature: Optional[float], - repetition_penalty: Optional[float], + typical_p: float | None, + temperature: float | None, + repetition_penalty: float | None, ) -> HuggingFaceEndpoint: retry_attempts = self.retry_attempts endpoint_url = self.get_api_url() diff --git a/src/backend/base/langflow/components/prompts/LangChainHubPrompt.py b/src/backend/base/langflow/components/prompts/LangChainHubPrompt.py index 4a71d0642..0609d06bb 100644 --- a/src/backend/base/langflow/components/prompts/LangChainHubPrompt.py +++ b/src/backend/base/langflow/components/prompts/LangChainHubPrompt.py @@ -1,5 +1,6 @@ import re -from typing import List + +from langchain_core.prompts import HumanMessagePromptTemplate from langchain_core.prompts import HumanMessagePromptTemplate @@ -55,7 +56,7 @@ class LangChainHubPromptComponent(Component): pattern = r"\{(.*?)\}" # Get all the custom fields - custom_fields: List[str] = [] + custom_fields: list[str] = [] full_template = "" for message in prompt_template: # Find all matches diff --git a/src/backend/base/langflow/components/prototypes/FlowTool.py b/src/backend/base/langflow/components/prototypes/FlowTool.py index 4a75bf11b..718330fed 100644 --- a/src/backend/base/langflow/components/prototypes/FlowTool.py +++ b/src/backend/base/langflow/components/prototypes/FlowTool.py @@ -1,5 +1,5 @@ import warnings -from typing import Any, List, Optional +from typing import Any from langflow.base.langchain_utilities.model import LCToolComponent from langflow.base.tools.flow_tool import FlowTool @@ -19,11 +19,11 @@ class FlowToolComponent(LCToolComponent): name = "FlowTool" beta = True - def get_flow_names(self) -> List[str]: + def get_flow_names(self) -> list[str]: flow_datas = self.list_flows() return [flow_data.data["name"] for flow_data in flow_datas] - def get_flow(self, flow_name: str) -> Optional[Data]: + def get_flow(self, flow_name: str) -> Data | None: """ Retrieves a flow by its name. diff --git a/src/backend/base/langflow/components/prototypes/Notify.py b/src/backend/base/langflow/components/prototypes/Notify.py index 72287a255..f682abbfb 100644 --- a/src/backend/base/langflow/components/prototypes/Notify.py +++ b/src/backend/base/langflow/components/prototypes/Notify.py @@ -1,5 +1,3 @@ -from typing import Optional - from langflow.custom import CustomComponent from langflow.schema import Data @@ -21,7 +19,7 @@ class NotifyComponent(CustomComponent): }, } - def build(self, name: str, data: Optional[Data] = None, append: bool = False) -> Data: + def build(self, name: str, data: Data | None = None, append: bool = False) -> Data: if data and not isinstance(data, Data): if isinstance(data, str): data = Data(text=data) diff --git a/src/backend/base/langflow/components/prototypes/PythonFunction.py b/src/backend/base/langflow/components/prototypes/PythonFunction.py index 5d572eacb..cf37e39ce 100644 --- a/src/backend/base/langflow/components/prototypes/PythonFunction.py +++ b/src/backend/base/langflow/components/prototypes/PythonFunction.py @@ -1,4 +1,4 @@ -from typing import Callable, List +from collections.abc import Callable from langflow.custom import Component from langflow.custom.utils import get_function @@ -46,7 +46,7 @@ class PythonFunctionComponent(Component): func = get_function(function_code) return func - def execute_function(self) -> List[dotdict | str] | dotdict | str: + def execute_function(self) -> list[dotdict | str] | dotdict | str: function_code = self.function_code if not function_code: @@ -58,7 +58,7 @@ class PythonFunctionComponent(Component): except Exception as e: return f"Error executing function: {str(e)}" - def execute_function_data(self) -> List[Data]: + def execute_function_data(self) -> list[Data]: results = self.execute_function() results = results if isinstance(results, list) else [results] data = [(Data(text=x) if isinstance(x, str) else Data(**x)) for x in results] diff --git a/src/backend/base/langflow/components/prototypes/RunFlow.py b/src/backend/base/langflow/components/prototypes/RunFlow.py index 11c997696..56e5f74db 100644 --- a/src/backend/base/langflow/components/prototypes/RunFlow.py +++ b/src/backend/base/langflow/components/prototypes/RunFlow.py @@ -1,4 +1,4 @@ -from typing import Any, List, Optional +from typing import Any from langflow.base.flow_processing.utils import build_data_from_run_outputs from langflow.custom import Component @@ -13,7 +13,7 @@ class RunFlowComponent(Component): name = "RunFlow" beta: bool = True - def get_flow_names(self) -> List[str]: + def get_flow_names(self) -> list[str]: flow_data = self.list_flows() return [flow_data.data["name"] for flow_data in flow_data] @@ -47,12 +47,12 @@ class RunFlowComponent(Component): Output(display_name="Run Outputs", name="run_outputs", method="generate_results"), ] - async def generate_results(self) -> List[Data]: + async def generate_results(self) -> list[Data]: if "flow_name" not in self._attributes or not self._attributes["flow_name"]: raise ValueError("Flow name is required") flow_name = self._attributes["flow_name"] - results: List[Optional[RunOutputs]] = await self.run_flow( + results: list[RunOutputs | None] = await self.run_flow( inputs={"input_value": self.input_value}, flow_name=flow_name, tweaks=self.tweaks ) if isinstance(results, list): diff --git a/src/backend/base/langflow/components/prototypes/SubFlow.py b/src/backend/base/langflow/components/prototypes/SubFlow.py index 4521a06ea..728622f07 100644 --- a/src/backend/base/langflow/components/prototypes/SubFlow.py +++ b/src/backend/base/langflow/components/prototypes/SubFlow.py @@ -1,4 +1,4 @@ -from typing import Any, List, Optional +from typing import Any from loguru import logger @@ -17,11 +17,11 @@ class SubFlowComponent(Component): name = "SubFlow" beta: bool = True - def get_flow_names(self) -> List[str]: + def get_flow_names(self) -> list[str]: flow_data = self.list_flows() return [flow_data.data["name"] for flow_data in flow_data] - def get_flow(self, flow_name: str) -> Optional[Data]: + def get_flow(self, flow_name: str) -> Data | None: flow_datas = self.list_flows() for flow_data in flow_datas: if flow_data.data["name"] == flow_name: @@ -50,7 +50,7 @@ class SubFlowComponent(Component): return build_config - def add_inputs_to_build_config(self, inputs_vertex: List[Vertex], build_config: dotdict): + def add_inputs_to_build_config(self, inputs_vertex: list[Vertex], build_config: dotdict): new_fields: list[dotdict] = [] for vertex in inputs_vertex: @@ -81,7 +81,7 @@ class SubFlowComponent(Component): outputs = [Output(name="flow_outputs", display_name="Flow Outputs", method="generate_results")] - async def generate_results(self) -> List[Data]: + async def generate_results(self) -> list[Data]: tweaks: dict = {} for field in self._attributes.keys(): if field != "flow_name": diff --git a/src/backend/base/langflow/components/retrievers/AmazonKendra.py b/src/backend/base/langflow/components/retrievers/AmazonKendra.py index 8c4252c26..86f9b6c9c 100644 --- a/src/backend/base/langflow/components/retrievers/AmazonKendra.py +++ b/src/backend/base/langflow/components/retrievers/AmazonKendra.py @@ -1,4 +1,4 @@ -from typing import Optional, cast +from typing import cast from langchain_community.retrievers import AmazonKendraRetriever @@ -33,10 +33,10 @@ class AmazonKendraRetrieverComponent(CustomComponent): self, index_id: str, top_k: int = 3, - region_name: Optional[str] = None, - credentials_profile_name: Optional[str] = None, - attribute_filter: Optional[dict] = None, - user_context: Optional[dict] = None, + region_name: str | None = None, + credentials_profile_name: str | None = None, + attribute_filter: dict | None = None, + user_context: dict | None = None, ) -> Retriever: # type: ignore[type-var] try: output = AmazonKendraRetriever( diff --git a/src/backend/base/langflow/components/retrievers/CohereRerank.py b/src/backend/base/langflow/components/retrievers/CohereRerank.py index a315532a1..5a50ac445 100644 --- a/src/backend/base/langflow/components/retrievers/CohereRerank.py +++ b/src/backend/base/langflow/components/retrievers/CohereRerank.py @@ -1,4 +1,4 @@ -from typing import List, cast +from typing import cast from langchain.retrievers import ContextualCompressionRetriever from langchain_cohere import CohereRerank @@ -73,7 +73,7 @@ class CohereRerankComponent(LCVectorStoreComponent): retriever = ContextualCompressionRetriever(base_compressor=cohere_reranker, base_retriever=self.retriever) return cast(Retriever, retriever) - async def search_documents(self) -> List[Data]: # type: ignore + async def search_documents(self) -> list[Data]: # type: ignore retriever = self.build_base_retriever() documents = await retriever.ainvoke(self.search_query, config={"callbacks": self.get_langchain_callbacks()}) data = self.to_data(documents) diff --git a/src/backend/base/langflow/components/retrievers/MetalRetriever.py b/src/backend/base/langflow/components/retrievers/MetalRetriever.py index f5ce37d05..30e9876f7 100644 --- a/src/backend/base/langflow/components/retrievers/MetalRetriever.py +++ b/src/backend/base/langflow/components/retrievers/MetalRetriever.py @@ -1,4 +1,4 @@ -from typing import Optional, cast +from typing import cast from langchain_community.retrievers import MetalRetriever from metal_sdk.metal import Metal # type: ignore @@ -21,7 +21,7 @@ class MetalRetrieverComponent(CustomComponent): "code": {"show": False}, } - def build(self, api_key: str, client_id: str, index_id: str, params: Optional[dict] = None) -> Retriever: # type: ignore[type-var] + def build(self, api_key: str, client_id: str, index_id: str, params: dict | None = None) -> Retriever: # type: ignore[type-var] try: metal = Metal(api_key=api_key, client_id=client_id, index_id=index_id) except Exception as e: diff --git a/src/backend/base/langflow/components/retrievers/MultiQueryRetriever.py b/src/backend/base/langflow/components/retrievers/MultiQueryRetriever.py index c662f701e..27fcd1796 100644 --- a/src/backend/base/langflow/components/retrievers/MultiQueryRetriever.py +++ b/src/backend/base/langflow/components/retrievers/MultiQueryRetriever.py @@ -1,5 +1,3 @@ -from typing import Optional - from langchain.retrievers import MultiQueryRetriever from langflow.custom import CustomComponent @@ -42,7 +40,7 @@ class MultiQueryRetrieverComponent(CustomComponent): self, llm: LanguageModel, retriever: BaseRetriever, - prompt: Optional[Text] = None, + prompt: Text | None = None, parser_key: str = "lines", ) -> MultiQueryRetriever: if not prompt: diff --git a/src/backend/base/langflow/components/retrievers/NvidiaRerank.py b/src/backend/base/langflow/components/retrievers/NvidiaRerank.py index a654cf3f9..c8befbc9a 100644 --- a/src/backend/base/langflow/components/retrievers/NvidiaRerank.py +++ b/src/backend/base/langflow/components/retrievers/NvidiaRerank.py @@ -1,4 +1,4 @@ -from typing import Any, List, cast +from typing import Any, cast from langchain.retrievers import ContextualCompressionRetriever @@ -70,7 +70,7 @@ class NvidiaRerankComponent(LCVectorStoreComponent): retriever = ContextualCompressionRetriever(base_compressor=nvidia_reranker, base_retriever=self.retriever) return cast(Retriever, retriever) - async def search_documents(self) -> List[Data]: # type: ignore + async def search_documents(self) -> list[Data]: # type: ignore retriever = self.build_base_retriever() documents = await retriever.ainvoke(self.search_query, config={"callbacks": self.get_langchain_callbacks()}) data = self.to_data(documents) diff --git a/src/backend/base/langflow/components/retrievers/SelfQueryRetriever.py b/src/backend/base/langflow/components/retrievers/SelfQueryRetriever.py index b75829cb4..859b20f6c 100644 --- a/src/backend/base/langflow/components/retrievers/SelfQueryRetriever.py +++ b/src/backend/base/langflow/components/retrievers/SelfQueryRetriever.py @@ -1,5 +1,3 @@ -from typing import List - from langchain.chains.query_constructor.base import AttributeInfo from langchain.retrievers.self_query.base import SelfQueryRetriever @@ -53,7 +51,7 @@ class SelfQueryRetrieverComponent(Component): Output(display_name="Retrieved Documents", name="documents", method="retrieve_documents"), ] - def retrieve_documents(self) -> List[Data]: + def retrieve_documents(self) -> list[Data]: metadata_field_infos = [AttributeInfo(**value.data) for value in self.attribute_infos] self_query_retriever = SelfQueryRetriever.from_llm( llm=self.llm, diff --git a/src/backend/base/langflow/components/retrievers/VectaraSelfQueryRetriver.py b/src/backend/base/langflow/components/retrievers/VectaraSelfQueryRetriver.py index d8580b55a..f4647cee2 100644 --- a/src/backend/base/langflow/components/retrievers/VectaraSelfQueryRetriver.py +++ b/src/backend/base/langflow/components/retrievers/VectaraSelfQueryRetriver.py @@ -1,5 +1,5 @@ import json -from typing import List, cast +from typing import cast from langchain.chains.query_constructor.base import AttributeInfo from langchain.retrievers.self_query.base import SelfQueryRetriever @@ -40,7 +40,7 @@ class VectaraSelfQueryRetriverComponent(CustomComponent): vectorstore: VectorStore, document_content_description: str, llm: LanguageModel, - metadata_field_info: List[str], + metadata_field_info: list[str], ) -> Retriever: # type: ignore metadata_field_obj = [] diff --git a/src/backend/base/langflow/components/toolkits/ComposioAPI.py b/src/backend/base/langflow/components/toolkits/ComposioAPI.py index 60a11d2f5..b9391a182 100644 --- a/src/backend/base/langflow/components/toolkits/ComposioAPI.py +++ b/src/backend/base/langflow/components/toolkits/ComposioAPI.py @@ -1,4 +1,5 @@ -from typing import Any, Sequence +from collections.abc import Sequence +from typing import Any from composio_langchain import Action, App, ComposioToolSet # type: ignore from langchain_core.tools import Tool diff --git a/src/backend/base/langflow/components/toolkits/Metaphor.py b/src/backend/base/langflow/components/toolkits/Metaphor.py index 73c764512..29c8ce9a3 100644 --- a/src/backend/base/langflow/components/toolkits/Metaphor.py +++ b/src/backend/base/langflow/components/toolkits/Metaphor.py @@ -1,5 +1,3 @@ -from typing import List, Union - from langchain_community.agent_toolkits.base import BaseToolkit from langchain_core.tools import Tool, tool from metaphor_python import Metaphor # type: ignore @@ -25,7 +23,7 @@ class MetaphorToolkit(CustomComponent): use_autoprompt: bool = True, search_num_results: int = 5, similar_num_results: int = 5, - ) -> Union[Tool, BaseToolkit]: + ) -> Tool | BaseToolkit: # If documents, then we need to create a Vectara instance using .from_documents client = Metaphor(api_key=metaphor_api_key) @@ -35,7 +33,7 @@ class MetaphorToolkit(CustomComponent): return client.search(query, use_autoprompt=use_autoprompt, num_results=search_num_results) @tool - def get_contents(ids: List[str]): + def get_contents(ids: list[str]): """Get contents of a webpage. The ids passed in should be a list of ids as fetched from `search`. diff --git a/src/backend/base/langflow/components/tools/BingSearchAPI.py b/src/backend/base/langflow/components/tools/BingSearchAPI.py index b246cf13c..d753f0dfd 100644 --- a/src/backend/base/langflow/components/tools/BingSearchAPI.py +++ b/src/backend/base/langflow/components/tools/BingSearchAPI.py @@ -1,4 +1,4 @@ -from typing import List, cast +from typing import cast from langchain_community.tools.bing_search import BingSearchResults from langchain_community.utilities import BingSearchAPIWrapper @@ -24,7 +24,7 @@ class BingSearchAPIComponent(LCToolComponent): IntInput(name="k", display_name="Number of results", value=4, required=True), ] - def run_model(self) -> List[Data]: + def run_model(self) -> list[Data]: if self.bing_search_url: wrapper = BingSearchAPIWrapper( bing_search_url=self.bing_search_url, bing_subscription_key=self.bing_subscription_key diff --git a/src/backend/base/langflow/components/tools/Calculator.py b/src/backend/base/langflow/components/tools/Calculator.py index 3a2ab5106..a85907d31 100644 --- a/src/backend/base/langflow/components/tools/Calculator.py +++ b/src/backend/base/langflow/components/tools/Calculator.py @@ -1,6 +1,5 @@ import ast import operator -from typing import List from langchain.tools import StructuredTool from pydantic import BaseModel, Field @@ -28,7 +27,7 @@ class CalculatorToolComponent(LCToolComponent): class CalculatorToolSchema(BaseModel): expression: str = Field(..., description="The arithmetic expression to evaluate.") - def run_model(self) -> List[Data]: + def run_model(self) -> list[Data]: return self._evaluate_expression(self.expression) def build_tool(self) -> Tool: @@ -39,7 +38,7 @@ class CalculatorToolComponent(LCToolComponent): args_schema=self.CalculatorToolSchema, ) - def _evaluate_expression(self, expression: str) -> List[Data]: + def _evaluate_expression(self, expression: str) -> list[Data]: try: # Define the allowed operators operators = { diff --git a/src/backend/base/langflow/components/tools/DuckDuckGoSearchRun.py b/src/backend/base/langflow/components/tools/DuckDuckGoSearchRun.py index c0ecddb63..de1a776c1 100644 --- a/src/backend/base/langflow/components/tools/DuckDuckGoSearchRun.py +++ b/src/backend/base/langflow/components/tools/DuckDuckGoSearchRun.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List +from typing import Any from langchain.tools import StructuredTool from langchain_community.tools import DuckDuckGoSearchRun @@ -37,7 +37,7 @@ class DuckDuckGoSearchComponent(LCToolComponent): def build_tool(self) -> Tool: wrapper = self._build_wrapper() - def search_func(query: str, max_results: int = 5, max_snippet_length: int = 100) -> List[Dict[str, Any]]: + def search_func(query: str, max_results: int = 5, max_snippet_length: int = 100) -> list[dict[str, Any]]: full_results = wrapper.run(f"{query} (site:*)") result_list = full_results.split("\n")[:max_results] limited_results = [] @@ -57,7 +57,7 @@ class DuckDuckGoSearchComponent(LCToolComponent): self.status = "DuckDuckGo Search Tool created" return tool - def run_model(self) -> List[Data]: + def run_model(self) -> list[Data]: tool = self.build_tool() results = tool.run( { diff --git a/src/backend/base/langflow/components/tools/GleanSearchAPI.py b/src/backend/base/langflow/components/tools/GleanSearchAPI.py index 18bbd7267..00db90499 100644 --- a/src/backend/base/langflow/components/tools/GleanSearchAPI.py +++ b/src/backend/base/langflow/components/tools/GleanSearchAPI.py @@ -1,5 +1,5 @@ import json -from typing import Any, Dict, Optional, Union +from typing import Any from urllib.parse import urljoin import httpx @@ -33,7 +33,7 @@ class GleanSearchAPIComponent(LCToolComponent): return Tool(name="glean_search_api", description="Search with the Glean API", func=wrapper.run) - def run_model(self) -> Union[Data, list[Data]]: + def run_model(self) -> Data | list[Data]: wrapper = self._build_wrapper() results = wrapper.results( @@ -67,7 +67,7 @@ class GleanSearchAPIComponent(LCToolComponent): self, query: str, page_size: int = 10, - request_options: Optional[Dict[str, Any]] = None, + request_options: dict[str, Any] | None = None, ) -> dict: # Ensure there's a trailing slash url = self.glean_api_url @@ -97,7 +97,7 @@ class GleanSearchAPIComponent(LCToolComponent): return results - def _search_api_results(self, query: str, **kwargs: Any) -> Dict[str, Any]: + def _search_api_results(self, query: str, **kwargs: Any) -> dict[str, Any]: request_details = self._prepare_request(query, **kwargs) response = httpx.post( diff --git a/src/backend/base/langflow/components/tools/GoogleSearchAPI.py b/src/backend/base/langflow/components/tools/GoogleSearchAPI.py index f0aa4d13f..dc9d31242 100644 --- a/src/backend/base/langflow/components/tools/GoogleSearchAPI.py +++ b/src/backend/base/langflow/components/tools/GoogleSearchAPI.py @@ -1,5 +1,3 @@ -from typing import Union - from langchain_core.tools import Tool from langflow.base.langchain_utilities.model import LCToolComponent @@ -22,7 +20,7 @@ class GoogleSearchAPIComponent(LCToolComponent): IntInput(name="k", display_name="Number of results", value=4, required=True), ] - def run_model(self) -> Union[Data, list[Data]]: + def run_model(self) -> Data | list[Data]: wrapper = self._build_wrapper() results = wrapper.results(query=self.input_value, num_results=self.k) data = [Data(data=result, text=result["snippet"]) for result in results] diff --git a/src/backend/base/langflow/components/tools/GoogleSerperAPI.py b/src/backend/base/langflow/components/tools/GoogleSerperAPI.py index 0b4c59e21..a7f53ce07 100644 --- a/src/backend/base/langflow/components/tools/GoogleSerperAPI.py +++ b/src/backend/base/langflow/components/tools/GoogleSerperAPI.py @@ -1,5 +1,3 @@ -from typing import Union - from langchain_community.utilities.google_serper import GoogleSerperAPIWrapper from langflow.base.langchain_utilities.model import LCToolComponent @@ -22,7 +20,7 @@ class GoogleSerperAPIComponent(LCToolComponent): IntInput(name="k", display_name="Number of results", value=4, required=True), ] - def run_model(self) -> Union[Data, list[Data]]: + def run_model(self) -> Data | list[Data]: wrapper = self._build_wrapper() results = wrapper.results(query=self.input_value) list_results = results.get("organic", []) diff --git a/src/backend/base/langflow/components/tools/PythonREPLTool.py b/src/backend/base/langflow/components/tools/PythonREPLTool.py index 772f6e235..4396d3818 100644 --- a/src/backend/base/langflow/components/tools/PythonREPLTool.py +++ b/src/backend/base/langflow/components/tools/PythonREPLTool.py @@ -1,5 +1,4 @@ import importlib -from typing import List, Union from langchain.tools import StructuredTool from langchain_experimental.utilities import PythonREPL @@ -46,7 +45,7 @@ class PythonREPLToolComponent(LCToolComponent): class PythonREPLSchema(BaseModel): code: str = Field(..., description="The Python code to execute.") - def get_globals(self, global_imports: Union[str, List[str]]) -> dict: + def get_globals(self, global_imports: str | list[str]) -> dict: global_dict = {} if isinstance(global_imports, str): modules = [module.strip() for module in global_imports.split(",")] @@ -83,7 +82,7 @@ class PythonREPLToolComponent(LCToolComponent): self.status = f"Python REPL Tool created with global imports: {self.global_imports}" return tool - def run_model(self) -> List[Data]: + def run_model(self) -> list[Data]: tool = self.build_tool() result = tool.run(self.code) return [Data(data={"result": result})] diff --git a/src/backend/base/langflow/components/tools/SearchAPI.py b/src/backend/base/langflow/components/tools/SearchAPI.py index ef04c9b83..2bb045d05 100644 --- a/src/backend/base/langflow/components/tools/SearchAPI.py +++ b/src/backend/base/langflow/components/tools/SearchAPI.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional +from typing import Any from langchain.tools import StructuredTool from langchain_community.utilities.searchapi import SearchApiAPIWrapper @@ -30,7 +30,7 @@ class SearchAPIComponent(LCToolComponent): class SearchAPISchema(BaseModel): query: str = Field(..., description="The search query") - params: Optional[Dict[str, Any]] = Field(default_factory=dict, description="Additional search parameters") + params: dict[str, Any] | None = Field(default_factory=dict, description="Additional search parameters") max_results: int = Field(5, description="Maximum number of results to return") max_snippet_length: int = Field(100, description="Maximum length of each result snippet") @@ -41,8 +41,8 @@ class SearchAPIComponent(LCToolComponent): wrapper = self._build_wrapper() def search_func( - query: str, params: Optional[Dict[str, Any]] = None, max_results: int = 5, max_snippet_length: int = 100 - ) -> List[Dict[str, Any]]: + query: str, params: dict[str, Any] | None = None, max_results: int = 5, max_snippet_length: int = 100 + ) -> list[dict[str, Any]]: params = params or {} full_results = wrapper.results(query=query, **params) organic_results = full_results.get("organic_results", [])[:max_results] @@ -68,7 +68,7 @@ class SearchAPIComponent(LCToolComponent): self.status = f"Search API Tool created with engine: {self.engine}" return tool - def run_model(self) -> List[Data]: + def run_model(self) -> list[Data]: tool = self.build_tool() results = tool.run( { diff --git a/src/backend/base/langflow/components/tools/SerpAPI.py b/src/backend/base/langflow/components/tools/SerpAPI.py index ff3232f1b..9fea66ff5 100644 --- a/src/backend/base/langflow/components/tools/SerpAPI.py +++ b/src/backend/base/langflow/components/tools/SerpAPI.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional +from typing import Any from langchain.tools import StructuredTool from langchain_community.utilities.serpapi import SerpAPIWrapper @@ -28,7 +28,7 @@ class SerpAPIComponent(LCToolComponent): class SerpAPISchema(BaseModel): query: str = Field(..., description="The search query") - params: Optional[Dict[str, Any]] = Field(default_factory=dict, description="Additional search parameters") + params: dict[str, Any] | None = Field(default_factory=dict, description="Additional search parameters") max_results: int = Field(5, description="Maximum number of results to return") max_snippet_length: int = Field(100, description="Maximum length of each result snippet") @@ -44,8 +44,8 @@ class SerpAPIComponent(LCToolComponent): wrapper = self._build_wrapper() def search_func( - query: str, params: Optional[Dict[str, Any]] = None, max_results: int = 5, max_snippet_length: int = 100 - ) -> List[Dict[str, Any]]: + query: str, params: dict[str, Any] | None = None, max_results: int = 5, max_snippet_length: int = 100 + ) -> list[dict[str, Any]]: params = params or {} full_results = wrapper.results(query, **params) organic_results = full_results.get("organic_results", [])[:max_results] @@ -71,7 +71,7 @@ class SerpAPIComponent(LCToolComponent): self.status = "SerpAPI Tool created" return tool - def run_model(self) -> List[Data]: + def run_model(self) -> list[Data]: tool = self.build_tool() try: results = tool.run( diff --git a/src/backend/base/langflow/components/vectorstores/Cassandra.py b/src/backend/base/langflow/components/vectorstores/Cassandra.py index 23374f2e0..e36a68930 100644 --- a/src/backend/base/langflow/components/vectorstores/Cassandra.py +++ b/src/backend/base/langflow/components/vectorstores/Cassandra.py @@ -1,5 +1,3 @@ -from typing import List - from langchain_community.vectorstores import Cassandra from loguru import logger @@ -219,7 +217,7 @@ class CassandraVectorStoreComponent(LCVectorStoreComponent): else: return "similarity" - def search_documents(self) -> List[Data]: + def search_documents(self) -> list[Data]: vector_store = self.build_vector_store() logger.debug(f"Search input: {self.search_query}") diff --git a/src/backend/base/langflow/components/vectorstores/CassandraGraph.py b/src/backend/base/langflow/components/vectorstores/CassandraGraph.py index ce3ee8aa3..589e84cc8 100644 --- a/src/backend/base/langflow/components/vectorstores/CassandraGraph.py +++ b/src/backend/base/langflow/components/vectorstores/CassandraGraph.py @@ -1,4 +1,3 @@ -from typing import List from uuid import UUID from langchain_community.graph_vectorstores import CassandraGraphVectorStore @@ -197,7 +196,7 @@ class CassandraGraphVectorStoreComponent(LCVectorStoreComponent): else: return "traversal" - def search_documents(self) -> List[Data]: + def search_documents(self) -> list[Data]: vector_store = self.build_vector_store() logger.debug(f"Search input: {self.search_query}") diff --git a/src/backend/base/langflow/components/vectorstores/Clickhouse.py b/src/backend/base/langflow/components/vectorstores/Clickhouse.py index 4ebf78833..091a470c4 100644 --- a/src/backend/base/langflow/components/vectorstores/Clickhouse.py +++ b/src/backend/base/langflow/components/vectorstores/Clickhouse.py @@ -1,5 +1,3 @@ -from typing import List - from langchain_community.vectorstores import Clickhouse, ClickhouseSettings from langflow.base.vectorstores.model import LCVectorStoreComponent, check_cached_vector_store @@ -117,7 +115,7 @@ class ClickhouseVectorStoreComponent(LCVectorStoreComponent): return clickhouse_vs - def search_documents(self) -> List[Data]: + def search_documents(self) -> list[Data]: vector_store = self.build_vector_store() if self.search_query and isinstance(self.search_query, str) and self.search_query.strip(): diff --git a/src/backend/base/langflow/components/vectorstores/Couchbase.py b/src/backend/base/langflow/components/vectorstores/Couchbase.py index c9343c652..e313e10b3 100644 --- a/src/backend/base/langflow/components/vectorstores/Couchbase.py +++ b/src/backend/base/langflow/components/vectorstores/Couchbase.py @@ -1,5 +1,4 @@ from datetime import timedelta -from typing import List from langchain_community.vectorstores import CouchbaseVectorStore @@ -92,7 +91,7 @@ class CouchbaseVectorStoreComponent(LCVectorStoreComponent): return couchbase_vs - def search_documents(self) -> List[Data]: + def search_documents(self) -> list[Data]: vector_store = self.build_vector_store() if self.search_query and isinstance(self.search_query, str) and self.search_query.strip(): diff --git a/src/backend/base/langflow/components/vectorstores/FAISS.py b/src/backend/base/langflow/components/vectorstores/FAISS.py index 384e80190..66c0331cc 100644 --- a/src/backend/base/langflow/components/vectorstores/FAISS.py +++ b/src/backend/base/langflow/components/vectorstores/FAISS.py @@ -1,5 +1,3 @@ -from typing import List - from langchain_community.vectorstores import FAISS from loguru import logger @@ -79,7 +77,7 @@ class FaissVectorStoreComponent(LCVectorStoreComponent): return faiss - def search_documents(self) -> List[Data]: + def search_documents(self) -> list[Data]: """ Search for documents in the FAISS vector store. """ diff --git a/src/backend/base/langflow/components/vectorstores/Milvus.py b/src/backend/base/langflow/components/vectorstores/Milvus.py index d305ec74e..5391a0a31 100644 --- a/src/backend/base/langflow/components/vectorstores/Milvus.py +++ b/src/backend/base/langflow/components/vectorstores/Milvus.py @@ -1,5 +1,3 @@ -from typing import List - from langflow.base.vectorstores.model import LCVectorStoreComponent, check_cached_vector_store from langflow.helpers.data import docs_to_data from langflow.io import ( @@ -108,7 +106,7 @@ class MilvusVectorStoreComponent(LCVectorStoreComponent): return milvus_store - def search_documents(self) -> List[Data]: + def search_documents(self) -> list[Data]: vector_store = self.build_vector_store() if self.search_query and isinstance(self.search_query, str) and self.search_query.strip(): diff --git a/src/backend/base/langflow/components/vectorstores/MongoDBAtlasVector.py b/src/backend/base/langflow/components/vectorstores/MongoDBAtlasVector.py index 6f1b94402..c90a0dda7 100644 --- a/src/backend/base/langflow/components/vectorstores/MongoDBAtlasVector.py +++ b/src/backend/base/langflow/components/vectorstores/MongoDBAtlasVector.py @@ -1,5 +1,3 @@ -from typing import List - from langchain_community.vectorstores import MongoDBAtlasVectorSearch from langflow.base.vectorstores.model import LCVectorStoreComponent, check_cached_vector_store @@ -75,7 +73,7 @@ class MongoVectorStoreComponent(LCVectorStoreComponent): return vector_store - def search_documents(self) -> List[Data]: + def search_documents(self) -> list[Data]: from bson import ObjectId vector_store = self.build_vector_store() diff --git a/src/backend/base/langflow/components/vectorstores/Pinecone.py b/src/backend/base/langflow/components/vectorstores/Pinecone.py index b861e91d9..783141388 100644 --- a/src/backend/base/langflow/components/vectorstores/Pinecone.py +++ b/src/backend/base/langflow/components/vectorstores/Pinecone.py @@ -1,5 +1,3 @@ -from typing import List - from langchain_pinecone import Pinecone from langflow.base.vectorstores.model import LCVectorStoreComponent, check_cached_vector_store @@ -85,7 +83,7 @@ class PineconeVectorStoreComponent(LCVectorStoreComponent): pinecone.add_documents(documents) return pinecone - def search_documents(self) -> List[Data]: + def search_documents(self) -> list[Data]: vector_store = self.build_vector_store() if self.search_query and isinstance(self.search_query, str) and self.search_query.strip(): diff --git a/src/backend/base/langflow/components/vectorstores/Qdrant.py b/src/backend/base/langflow/components/vectorstores/Qdrant.py index 9c7b94f3e..223887d3b 100644 --- a/src/backend/base/langflow/components/vectorstores/Qdrant.py +++ b/src/backend/base/langflow/components/vectorstores/Qdrant.py @@ -1,5 +1,3 @@ -from typing import List - from langchain.embeddings.base import Embeddings from langchain_community.vectorstores import Qdrant @@ -99,7 +97,7 @@ class QdrantVectorStoreComponent(LCVectorStoreComponent): return qdrant - def search_documents(self) -> List[Data]: + def search_documents(self) -> list[Data]: vector_store = self.build_vector_store() if self.search_query and isinstance(self.search_query, str) and self.search_query.strip(): diff --git a/src/backend/base/langflow/components/vectorstores/Redis.py b/src/backend/base/langflow/components/vectorstores/Redis.py index 6dc9965f2..411b3759e 100644 --- a/src/backend/base/langflow/components/vectorstores/Redis.py +++ b/src/backend/base/langflow/components/vectorstores/Redis.py @@ -1,5 +1,3 @@ -from typing import List - from langchain.text_splitter import CharacterTextSplitter from langchain_community.vectorstores.redis import Redis @@ -79,7 +77,7 @@ class RedisVectorStoreComponent(LCVectorStoreComponent): ) return redis_vs - def search_documents(self) -> List[Data]: + def search_documents(self) -> list[Data]: vector_store = self.build_vector_store() if self.search_query and isinstance(self.search_query, str) and self.search_query.strip(): diff --git a/src/backend/base/langflow/components/vectorstores/SupabaseVectorStore.py b/src/backend/base/langflow/components/vectorstores/SupabaseVectorStore.py index cf1af0a60..9c9545baa 100644 --- a/src/backend/base/langflow/components/vectorstores/SupabaseVectorStore.py +++ b/src/backend/base/langflow/components/vectorstores/SupabaseVectorStore.py @@ -1,5 +1,3 @@ -from typing import List - from langchain_community.vectorstores import SupabaseVectorStore from supabase.client import Client, create_client @@ -66,7 +64,7 @@ class SupabaseVectorStoreComponent(LCVectorStoreComponent): return supabase_vs - def search_documents(self) -> List[Data]: + def search_documents(self) -> list[Data]: vector_store = self.build_vector_store() if self.search_query and isinstance(self.search_query, str) and self.search_query.strip(): diff --git a/src/backend/base/langflow/components/vectorstores/Upstash.py b/src/backend/base/langflow/components/vectorstores/Upstash.py index c0ecab2dc..eb1e6039e 100644 --- a/src/backend/base/langflow/components/vectorstores/Upstash.py +++ b/src/backend/base/langflow/components/vectorstores/Upstash.py @@ -1,5 +1,3 @@ -from typing import List - from langchain_community.vectorstores import UpstashVectorStore from langflow.base.vectorstores.model import LCVectorStoreComponent, check_cached_vector_store @@ -114,7 +112,7 @@ class UpstashVectorStoreComponent(LCVectorStoreComponent): return upstash_vs - def search_documents(self) -> List[Data]: + def search_documents(self) -> list[Data]: vector_store = self.build_vector_store() if self.search_query and isinstance(self.search_query, str) and self.search_query.strip(): diff --git a/src/backend/base/langflow/components/vectorstores/Vectara.py b/src/backend/base/langflow/components/vectorstores/Vectara.py index f077f11eb..a3600d8ec 100644 --- a/src/backend/base/langflow/components/vectorstores/Vectara.py +++ b/src/backend/base/langflow/components/vectorstores/Vectara.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, List +from typing import TYPE_CHECKING from langchain_community.vectorstores import Vectara from loguru import logger @@ -93,7 +93,7 @@ class VectaraVectorStoreComponent(LCVectorStoreComponent): logger.debug("No documents to add to Vectara.") self.status = "No valid documents to add to Vectara" - def search_documents(self) -> List[Data]: + def search_documents(self) -> list[Data]: vector_store = self.build_vector_store() if self.search_query and isinstance(self.search_query, str) and self.search_query.strip(): diff --git a/src/backend/base/langflow/components/vectorstores/Weaviate.py b/src/backend/base/langflow/components/vectorstores/Weaviate.py index 171558826..dd4109cec 100644 --- a/src/backend/base/langflow/components/vectorstores/Weaviate.py +++ b/src/backend/base/langflow/components/vectorstores/Weaviate.py @@ -1,5 +1,3 @@ -from typing import List - import weaviate # type: ignore from langchain_community.vectorstores import Weaviate @@ -70,7 +68,7 @@ class WeaviateVectorStoreComponent(LCVectorStoreComponent): by_text=self.search_by_text, ) - def search_documents(self) -> List[Data]: + def search_documents(self) -> list[Data]: vector_store = self.build_vector_store() if self.search_query and isinstance(self.search_query, str) and self.search_query.strip(): diff --git a/src/backend/base/langflow/components/vectorstores/pgvector.py b/src/backend/base/langflow/components/vectorstores/pgvector.py index 5c5668b9b..f8b4d47a9 100644 --- a/src/backend/base/langflow/components/vectorstores/pgvector.py +++ b/src/backend/base/langflow/components/vectorstores/pgvector.py @@ -1,5 +1,3 @@ -from typing import List - from langchain_community.vectorstores import PGVector from langflow.base.vectorstores.model import LCVectorStoreComponent, check_cached_vector_store @@ -63,7 +61,7 @@ class PGVectorStoreComponent(LCVectorStoreComponent): return pgvector - def search_documents(self) -> List[Data]: + def search_documents(self) -> list[Data]: vector_store = self.build_vector_store() if self.search_query and isinstance(self.search_query, str) and self.search_query.strip(): 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 0892193c0..9ceea82a3 100644 --- a/src/backend/base/langflow/custom/code_parser/code_parser.py +++ b/src/backend/base/langflow/custom/code_parser/code_parser.py @@ -41,7 +41,7 @@ def find_class_ast_node(class_obj): for node in ast.walk(tree): if isinstance(node, ast.ClassDef) and node.name == class_obj.__name__: class_node = node - elif isinstance(node, (ast.Import, ast.ImportFrom)): + elif isinstance(node, ast.Import | ast.ImportFrom): import_nodes.append(node) return class_node, import_nodes @@ -275,7 +275,7 @@ class CodeParser: or any(has_return(child) for child in node.handlers) or any(has_return(child) for child in node.finalbody) ) - elif isinstance(node, (ast.For, ast.While)): + elif isinstance(node, ast.For | ast.While): return any(has_return(child) for child in node.body) or any(has_return(child) for child in node.orelse) elif isinstance(node, ast.With): return any(has_return(child) for child in node.body) @@ -365,7 +365,7 @@ class CodeParser: elif isinstance(stmt, ast.AnnAssign): if attr := self.parse_ann_assign(stmt): class_details.attributes.append(attr) - elif isinstance(stmt, (ast.FunctionDef, ast.AsyncFunctionDef)): + elif isinstance(stmt, ast.FunctionDef | ast.AsyncFunctionDef): method, is_init = self.parse_function_def(stmt) if is_init: class_details.init = method diff --git a/src/backend/base/langflow/custom/custom_component/component.py b/src/backend/base/langflow/custom/custom_component/component.py index 814bdb3b9..3c6a38153 100644 --- a/src/backend/base/langflow/custom/custom_component/component.py +++ b/src/backend/base/langflow/custom/custom_component/component.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import inspect from collections.abc import Callable from copy import deepcopy @@ -33,7 +35,7 @@ CONFIG_ATTRIBUTES = ["_display_name", "_description", "_icon", "_name"] class Component(CustomComponent): - inputs: list["InputTypes"] = [] + inputs: list[InputTypes] = [] outputs: list[Output] = [] code_class_base_inheritance: ClassVar[str] = "Component" _output_logs: dict[str, Log] = {} @@ -51,7 +53,7 @@ class Component(CustomComponent): config[key[1:]] = value else: inputs[key] = value - self._inputs: dict[str, "InputTypes"] = {} + self._inputs: dict[str, InputTypes] = {} self._outputs_map: dict[str, Output] = {} self._results: dict[str, Any] = {} self._attributes: dict[str, Any] = {} @@ -179,7 +181,7 @@ class Component(CustomComponent): """ return await self._run() - def set_vertex(self, vertex: "Vertex"): + def set_vertex(self, vertex: Vertex): """ Sets the vertex for the component. @@ -258,7 +260,7 @@ class Component(CustomComponent): # allows each instance of each component to modify its own output self._outputs_map[output.name] = deepcopy(output) - def map_inputs(self, inputs: list["InputTypes"]): + def map_inputs(self, inputs: list[InputTypes]): """ Maps the given inputs to the component. @@ -325,7 +327,7 @@ class Component(CustomComponent): text += f"{output.name}[{','.join(output.types)}]->{input_.name}[{','.join(input_.input_types or [])}]\n" return text - def _find_matching_output_method(self, value: "Component"): + def _find_matching_output_method(self, value: Component): # get all outputs of the value component outputs = value._outputs_map.values() # check if the any of the types in the output.types matches ONLY one input in the current component @@ -652,7 +654,7 @@ class Component(CustomComponent): output.value = result custom_repr = self.custom_repr() - if custom_repr is None and isinstance(result, (dict, Data, str)): + if custom_repr is None and isinstance(result, dict | Data | str): custom_repr = result if not isinstance(custom_repr, str): custom_repr = str(custom_repr) @@ -670,7 +672,7 @@ class Component(CustomComponent): elif hasattr(raw, "model_dump") and raw is not None: raw = raw.model_dump() - if raw is None and isinstance(result, (dict, Data, str)): + if raw is None and isinstance(result, dict | Data | str): raw = result.data if isinstance(result, Data) else result artifact_type = get_artifact_type(artifact_value, result) raw, artifact_type = post_process_raw(raw, artifact_type) 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 781e24a0a..81c7d94c5 100644 --- a/src/backend/base/langflow/custom/custom_component/custom_component.py +++ b/src/backend/base/langflow/custom/custom_component/custom_component.py @@ -1,6 +1,8 @@ +from __future__ import annotations + from collections.abc import Callable, Sequence from pathlib import Path -from typing import TYPE_CHECKING, Any, ClassVar, Optional +from typing import TYPE_CHECKING, Any, ClassVar import yaml from cachetools import TTLCache @@ -69,7 +71,7 @@ class CustomComponent(BaseComponent): """The default frozen state of the component. Defaults to False.""" build_parameters: dict | None = None """The build parameters of the component. Defaults to None.""" - _vertex: Optional["Vertex"] = None + _vertex: Vertex | None = None """The edge target parameter of the component. Defaults to None.""" _code_class_base_inheritance: ClassVar[str] = "CustomComponent" function_entrypoint_name: ClassVar[str] = "build" @@ -81,7 +83,7 @@ class CustomComponent(BaseComponent): _outputs: list[OutputValue] = [] _logs: list[Log] = [] _output_logs: dict[str, Log] = {} - _tracing_service: Optional["TracingService"] = None + _tracing_service: TracingService | None = None _tree: dict | None = None def __init__(self, **data): @@ -158,7 +160,7 @@ class CustomComponent(BaseComponent): return str(path_object) def get_full_path(self, path: str) -> str: - storage_svc: "StorageService" = get_storage_service() + storage_svc: StorageService = get_storage_service() flow_id, file_name = path.split("/", 1) return storage_svc.build_full_path(flow_id, file_name) @@ -464,7 +466,7 @@ class CustomComponent(BaseComponent): """ return validate.create_function(self._code, self._function_entrypoint_name) - async def load_flow(self, flow_id: str, tweaks: dict | None = None) -> "Graph": + async def load_flow(self, flow_id: str, tweaks: dict | None = None) -> Graph: if not self.user_id: raise ValueError("Session is invalid") return await load_flow(user_id=str(self._user_id), flow_id=flow_id, tweaks=tweaks) @@ -517,7 +519,7 @@ class CustomComponent(BaseComponent): ) return frontend_node - def get_langchain_callbacks(self) -> list["BaseCallbackHandler"]: + def get_langchain_callbacks(self) -> list[BaseCallbackHandler]: if self._tracing_service: return self._tracing_service.get_langchain_callbacks() return [] diff --git a/src/backend/base/langflow/field_typing/constants.py b/src/backend/base/langflow/field_typing/constants.py index dfa8309e7..e4de1784a 100644 --- a/src/backend/base/langflow/field_typing/constants.py +++ b/src/backend/base/langflow/field_typing/constants.py @@ -1,4 +1,5 @@ -from typing import Callable, Dict, Text, TypeAlias, TypeVar, Union +from collections.abc import Callable +from typing import Text, TypeAlias, TypeVar from langchain.agents.agent import AgentExecutor from langchain.chains.base import Chain @@ -20,7 +21,7 @@ from langchain_text_splitters import TextSplitter from langflow.schema.data import Data from langflow.schema.message import Message -NestedDict: TypeAlias = Dict[str, Union[str, Dict]] +NestedDict: TypeAlias = dict[str, str | dict] LanguageModel = TypeVar("LanguageModel", BaseLanguageModel, BaseLLM, BaseChatModel) ToolEnabledLanguageModel = TypeVar("ToolEnabledLanguageModel", BaseLanguageModel, BaseLLM, BaseChatModel) Retriever = TypeVar( @@ -66,7 +67,7 @@ CUSTOM_COMPONENT_SUPPORTED_TYPES = { "NestedDict": NestedDict, "Data": Data, "Message": Message, - "Text": Text, + "Text": Text, # noqa: UP019 "Object": Object, "Callable": Callable, "LanguageModel": LanguageModel, diff --git a/src/backend/base/langflow/graph/graph/base.py b/src/backend/base/langflow/graph/graph/base.py index d307a801c..269a76391 100644 --- a/src/backend/base/langflow/graph/graph/base.py +++ b/src/backend/base/langflow/graph/graph/base.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import asyncio import copy import json @@ -8,7 +10,7 @@ from collections.abc import Generator, Iterable from datetime import datetime, timezone from functools import partial from itertools import chain -from typing import TYPE_CHECKING, Any, Optional, cast +from typing import TYPE_CHECKING, Any, cast import nest_asyncio from loguru import logger @@ -53,8 +55,8 @@ class Graph: def __init__( self, - start: Optional["Component"] = None, - end: Optional["Component"] = None, + start: Component | None = None, + end: Component | None = None, flow_id: str | None = None, flow_name: str | None = None, description: str | None = None, @@ -115,7 +117,7 @@ class Graph: self._call_order: list[str] = [] self._snapshots: list[dict[str, Any]] = [] try: - self.tracing_service: "TracingService" | None = get_tracing_service() + self.tracing_service: TracingService | None = get_tracing_service() except Exception as exc: logger.error(f"Error getting tracing service: {exc}") self.tracing_service = None @@ -203,7 +205,7 @@ class Graph: self._edges = self._graph_data["edges"] self.initialize() - def add_component(self, component: "Component", component_id: Optional[str] = None) -> str: + def add_component(self, component: Component, component_id: str | None = None) -> str: component_id = component_id or component._id if component_id in self.vertex_map: return component_id @@ -225,7 +227,7 @@ class Graph: return component_id - def _set_start_and_end(self, start: "Component", end: "Component"): + def _set_start_and_end(self, start: Component, end: Component): if not hasattr(start, "to_frontend_node"): raise TypeError(f"start must be a Component. Got {type(start)}") if not hasattr(end, "to_frontend_node"): @@ -613,7 +615,7 @@ class Graph: stream: bool, session_id: str, fallback_to_env_vars: bool, - ) -> list[Optional["ResultData"]]: + ) -> list[ResultData | None]: """ Runs the graph with the given inputs. @@ -808,7 +810,7 @@ class Graph: "flow_name": self.flow_name, } - def build_graph_maps(self, edges: list[CycleEdge] | None = None, vertices: list["Vertex"] | None = None): + def build_graph_maps(self, edges: list[CycleEdge] | None = None, vertices: list[Vertex] | None = None): """ Builds the adjacency maps for the graph. """ @@ -878,7 +880,7 @@ class Graph: return edge return None - def build_parent_child_map(self, vertices: list["Vertex"]): + def build_parent_child_map(self, vertices: list[Vertex]): parent_child_map = defaultdict(list) for vertex in vertices: parent_child_map[vertex.id] = [child.id for child in self.get_successors(vertex)] @@ -977,7 +979,7 @@ class Graph: flow_id: str | None = None, flow_name: str | None = None, user_id: str | None = None, - ) -> "Graph": + ) -> Graph: """ Creates a graph from a payload. @@ -1016,7 +1018,7 @@ class Graph: # both graphs have the same vertices and edges # but the data of the vertices might be different - def update_edges_from_vertex(self, vertex: "Vertex", other_vertex: "Vertex") -> None: + def update_edges_from_vertex(self, vertex: Vertex, other_vertex: Vertex) -> None: """Updates the edges of a vertex in the Graph.""" new_edges = [] for edge in self.edges: @@ -1026,13 +1028,13 @@ class Graph: new_edges += other_vertex.edges self.edges = new_edges - def vertex_data_is_identical(self, vertex: "Vertex", other_vertex: "Vertex") -> bool: + def vertex_data_is_identical(self, vertex: Vertex, other_vertex: Vertex) -> bool: data_is_equivalent = vertex == other_vertex if not data_is_equivalent: return False return self.vertex_edges_are_identical(vertex, other_vertex) - def vertex_edges_are_identical(self, vertex: "Vertex", other_vertex: "Vertex") -> bool: + def vertex_edges_are_identical(self, vertex: Vertex, other_vertex: Vertex) -> bool: same_length = len(vertex.edges) == len(other_vertex.edges) if not same_length: return False @@ -1041,7 +1043,7 @@ class Graph: return False return True - def update(self, other: "Graph") -> "Graph": + def update(self, other: Graph) -> Graph: # Existing vertices in self graph existing_vertex_ids = {vertex.id for vertex in self.vertices} # Vertex IDs in the other graph @@ -1091,7 +1093,7 @@ class Graph: self.increment_update_count() return self - def update_vertex_from_another(self, vertex: "Vertex", other_vertex: "Vertex") -> None: + def update_vertex_from_another(self, vertex: Vertex, other_vertex: Vertex) -> None: """ Updates a vertex from another vertex. @@ -1129,12 +1131,12 @@ class Graph: self.vertices.append(vertex) self.vertex_map[vertex.id] = vertex - def add_vertex(self, vertex: "Vertex") -> None: + def add_vertex(self, vertex: Vertex) -> None: """Adds a new vertex to the graph.""" self._add_vertex(vertex) self._update_edges(vertex) - def _update_edges(self, vertex: "Vertex") -> None: + def _update_edges(self, vertex: Vertex) -> None: """Updates the edges of a vertex.""" # Vertex has edges, so we need to update the edges for edge in vertex.edges: @@ -1169,19 +1171,19 @@ class Graph: for vertex in self.vertices: vertex._build_params() - def _validate_vertex(self, vertex: "Vertex") -> bool: + def _validate_vertex(self, vertex: Vertex) -> bool: """Validates a vertex.""" # All vertices that do not have edges are invalid return len(self.get_vertex_edges(vertex.id)) > 0 - def get_vertex(self, vertex_id: str, silent: bool = False) -> "Vertex": + def get_vertex(self, vertex_id: str, silent: bool = False) -> Vertex: """Returns a vertex by id.""" try: return self.vertex_map[vertex_id] except KeyError: raise ValueError(f"Vertex {vertex_id} not found") - def get_root_of_group_node(self, vertex_id: str) -> "Vertex": + def get_root_of_group_node(self, vertex_id: str) -> Vertex: """Returns the root of a group node.""" if vertex_id in self.top_level_vertices: # Get all vertices with vertex_id as .parent_node_id @@ -1205,7 +1207,7 @@ class Graph: async def astep( self, - inputs: Optional["InputValueRequest"] = None, + inputs: InputValueRequest | None = None, files: list[str] | None = None, user_id: str | None = None, event_manager: EventManager | None = None, @@ -1259,7 +1261,7 @@ class Graph: def step( self, - inputs: Optional["InputValueRequest"] = None, + inputs: InputValueRequest | None = None, files: list[str] | None = None, user_id: str | None = None, ): @@ -1382,9 +1384,9 @@ class Graph: or (edge.target_id == vertex_id and is_target is not False) ] - def get_vertices_with_target(self, vertex_id: str) -> list["Vertex"]: + def get_vertices_with_target(self, vertex_id: str) -> list[Vertex]: """Returns the vertices connected to a vertex.""" - vertices: list["Vertex"] = [] + vertices: list[Vertex] = [] for edge in self.edges: if edge.target_id == vertex_id: vertex = self.get_vertex(edge.source_id) @@ -1393,7 +1395,7 @@ class Graph: vertices.append(vertex) return vertices - async def process(self, fallback_to_env_vars: bool, start_component_id: str | None = None) -> "Graph": + async def process(self, fallback_to_env_vars: bool, start_component_id: str | None = None) -> Graph: """Processes the graph with vertices in each layer run in parallel.""" first_layer = self.sort_vertices(start_component_id=start_component_id) @@ -1450,7 +1452,7 @@ class Graph: return list(next_runnable_vertices) - async def get_next_runnable_vertices(self, lock: asyncio.Lock, vertex: "Vertex", cache: bool = True) -> list[str]: + async def get_next_runnable_vertices(self, lock: asyncio.Lock, vertex: Vertex, cache: bool = True) -> list[str]: v_id = vertex.id v_successors_ids = vertex.successors_ids async with lock: @@ -1471,7 +1473,7 @@ class Graph: """Executes tasks in parallel, handling exceptions for each task.""" results = [] completed_tasks = await asyncio.gather(*tasks, return_exceptions=True) - vertices: list["Vertex"] = [] + vertices: list[Vertex] = [] for i, result in enumerate(completed_tasks): task_name = tasks[i].get_name() @@ -1500,7 +1502,7 @@ class Graph: no_duplicate_results = list(set(results)) return no_duplicate_results - def topological_sort(self) -> list["Vertex"]: + def topological_sort(self) -> list[Vertex]: """ Performs a topological sort of the vertices in the graph. @@ -1533,7 +1535,7 @@ class Graph: return list(reversed(sorted_vertices)) - def generator_build(self) -> Generator["Vertex", None, None]: + def generator_build(self) -> Generator[Vertex, None, None]: """Builds each vertex in the graph and yields it.""" sorted_vertices = self.topological_sort() logger.debug("There are %s vertices in the graph", len(sorted_vertices)) @@ -1543,7 +1545,7 @@ class Graph: """Returns the predecessors of a vertex.""" return [self.get_vertex(source_id) for source_id in self.predecessor_map.get(vertex.id, [])] - def get_all_successors(self, vertex: "Vertex", recursive=True, flat=True, visited=None): + def get_all_successors(self, vertex: Vertex, recursive=True, flat=True, visited=None): if visited is None: visited = set() @@ -1576,13 +1578,13 @@ class Graph: return successors_result - def get_successors(self, vertex: "Vertex") -> list["Vertex"]: + def get_successors(self, vertex: Vertex) -> list[Vertex]: """Returns the successors of a vertex.""" return [self.get_vertex(target_id) for target_id in self.successor_map.get(vertex.id, [])] - def get_vertex_neighbors(self, vertex: "Vertex") -> dict["Vertex", int]: + def get_vertex_neighbors(self, vertex: Vertex) -> dict[Vertex, int]: """Returns the neighbors of a vertex.""" - neighbors: dict["Vertex", int] = {} + neighbors: dict[Vertex, int] = {} for edge in self.edges: if edge.source_id == vertex.id: neighbor = self.get_vertex(edge.target_id) @@ -1638,7 +1640,7 @@ class Graph: new_edge = Edge(source, target, edge) return new_edge - def _get_vertex_class(self, node_type: str, node_base_type: str, node_id: str) -> type["Vertex"]: + def _get_vertex_class(self, node_type: str, node_base_type: str, node_id: str) -> type[Vertex]: """Returns the node class based on the node type.""" # First we check for the node_base_type node_name = node_id.split("-")[0] @@ -1655,9 +1657,9 @@ class Graph: return lazy_load_vertex_dict.VERTEX_TYPE_MAP[node_type] return Vertex - def _build_vertices(self) -> list["Vertex"]: + def _build_vertices(self) -> list[Vertex]: """Builds the vertices of the graph.""" - vertices: list["Vertex"] = [] + vertices: list[Vertex] = [] for frontend_data in self._vertices: if frontend_data.get("type") == NodeTypeEnum.NoteNode: continue @@ -1730,7 +1732,7 @@ class Graph: def layered_topological_sort( self, - vertices: list["Vertex"], + vertices: list[Vertex], filter_graphs: bool = False, ) -> list[list[str]]: """Performs a layered topological sort of the vertices in the graph.""" @@ -1986,7 +1988,7 @@ class Graph: runnable_vertices = [] visited = set() - def find_runnable_predecessors(predecessor: "Vertex"): + def find_runnable_predecessors(predecessor: Vertex): predecessor_id = predecessor.id if predecessor_id in visited: return diff --git a/src/backend/base/langflow/graph/graph/state_manager.py b/src/backend/base/langflow/graph/graph/state_manager.py index 778dc376d..8936b86de 100644 --- a/src/backend/base/langflow/graph/graph/state_manager.py +++ b/src/backend/base/langflow/graph/graph/state_manager.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from collections.abc import Callable from typing import TYPE_CHECKING @@ -12,7 +14,7 @@ if TYPE_CHECKING: class GraphStateManager: def __init__(self): try: - self.state_service: "StateService" = get_state_service() + self.state_service: StateService = get_state_service() except Exception as e: logger.debug(f"Error getting state service. Defaulting to InMemoryStateService: {e}") from langflow.services.state.service import InMemoryStateService diff --git a/src/backend/base/langflow/graph/utils.py b/src/backend/base/langflow/graph/utils.py index 08eedb88b..dcad5386c 100644 --- a/src/backend/base/langflow/graph/utils.py +++ b/src/backend/base/langflow/graph/utils.py @@ -70,7 +70,7 @@ def serialize_field(value): """Unified serialization function for handling both BaseModel and Document types, including handling lists of these types.""" - if isinstance(value, (list, tuple)): + if isinstance(value, list | tuple): return [serialize_field(v) for v in value] elif isinstance(value, Document): return value.to_json() @@ -126,12 +126,12 @@ def _vertex_to_primitive_dict(target: "Vertex") -> dict: """ # Removes all keys that the values aren't python types like str, int, bool, etc. params = { - key: value for key, value in target.params.items() if isinstance(value, (str, int, bool, float, list, dict)) + key: value for key, value in target.params.items() if isinstance(value, str | int | bool | float | list | dict) } # if it is a list we need to check if the contents are python types for key, value in params.items(): if isinstance(value, list): - params[key] = [item for item in value if isinstance(item, (str, int, bool, float, list, dict))] + params[key] = [item for item in value if isinstance(item, str | int | bool | float | list | dict)] return params diff --git a/src/backend/base/langflow/graph/vertex/base.py b/src/backend/base/langflow/graph/vertex/base.py index 761262d0e..96fcd08f0 100644 --- a/src/backend/base/langflow/graph/vertex/base.py +++ b/src/backend/base/langflow/graph/vertex/base.py @@ -150,7 +150,7 @@ class Vertex: result = self._built_object # if it is not a dict or a string and hasattr model_dump then # return the model_dump - if not isinstance(result, (dict, str)) and hasattr(result, "content"): + if not isinstance(result, dict | str) and hasattr(result, "content"): return result.content return result if isinstance(self._built_object, str): @@ -502,7 +502,7 @@ class Vertex: component_id = self.id _type = self.artifacts_type - if isinstance(sender_name, (Data, Message)): + if isinstance(sender_name, Data | Message): sender_name = sender_name.get_text() messages = [ @@ -737,7 +737,7 @@ class Vertex: message += " Make sure your build method returns a component." logger.warning(message) - elif isinstance(self._built_object, (Iterator, AsyncIterator)): + elif isinstance(self._built_object, Iterator | AsyncIterator): if self.display_name in ["Text Output"]: raise ValueError(f"You are trying to stream to a {self.display_name}. Try using a Chat Output instead.") diff --git a/src/backend/base/langflow/graph/vertex/types.py b/src/backend/base/langflow/graph/vertex/types.py index 6a6dd0534..466e049ea 100644 --- a/src/backend/base/langflow/graph/vertex/types.py +++ b/src/backend/base/langflow/graph/vertex/types.py @@ -270,7 +270,7 @@ class InterfaceVertex(ComponentVertex): text_output = self.results["message"].text else: text_output = message - if isinstance(text_output, (AIMessage, AIMessageChunk)): + if isinstance(text_output, AIMessage | AIMessageChunk): artifacts = ChatOutputResponse.from_message( text_output, sender=sender, @@ -283,7 +283,7 @@ class InterfaceVertex(ComponentVertex): message = dict_to_codeblock(text_output) elif isinstance(text_output, Data): message = text_output.text - elif isinstance(message, (AsyncIterator, Iterator)): + elif isinstance(message, AsyncIterator | Iterator): stream_url = self.build_stream_url() message = "" self.results["text"] = message @@ -357,7 +357,7 @@ class InterfaceVertex(ComponentVertex): message = self._process_chat_component() elif self.vertex_type in RECORDS_COMPONENTS: message = self._process_data_component() - if isinstance(self._built_object, (AsyncIterator, Iterator)): + if isinstance(self._built_object, AsyncIterator | Iterator): if self.params.get("return_data", False): self._built_object = Data(text=message, data=self.artifacts) else: @@ -369,7 +369,7 @@ class InterfaceVertex(ComponentVertex): async def stream(self): iterator = self.params.get(INPUT_FIELD_NAME, None) - if not isinstance(iterator, (AsyncIterator, Iterator)): + if not isinstance(iterator, AsyncIterator | Iterator): raise ValueError("The message must be an iterator or an async iterator.") is_async = isinstance(iterator, AsyncIterator) complete_message = "" @@ -415,7 +415,7 @@ class InterfaceVertex(ComponentVertex): self.params[INPUT_FIELD_NAME] = complete_message if isinstance(self._built_object, dict): for key, value in self._built_object.items(): - if hasattr(value, "text") and (isinstance(value.text, (AsyncIterator, Iterator)) or value.text == ""): + if hasattr(value, "text") and (isinstance(value.text, AsyncIterator | Iterator) or value.text == ""): self._built_object[key] = message else: self._built_object = message @@ -430,7 +430,7 @@ class InterfaceVertex(ComponentVertex): for edge in edges: origin_vertex = self.graph.get_vertex(edge.source_id) for key, value in origin_vertex.results.items(): - if isinstance(value, (AsyncIterator, Iterator)): + if isinstance(value, AsyncIterator | Iterator): origin_vertex.results[key] = complete_message if self._custom_component: if hasattr(self._custom_component, "should_store_message") and hasattr( diff --git a/src/backend/base/langflow/graph/vertex/utils.py b/src/backend/base/langflow/graph/vertex/utils.py index 0f69e4b2d..d68b1b2fd 100644 --- a/src/backend/base/langflow/graph/vertex/utils.py +++ b/src/backend/base/langflow/graph/vertex/utils.py @@ -10,10 +10,10 @@ def build_clean_params(target: "Vertex") -> dict: """ # Removes all keys that the values aren't python types like str, int, bool, etc. params = { - key: value for key, value in target.params.items() if isinstance(value, (str, int, bool, float, list, dict)) + key: value for key, value in target.params.items() if isinstance(value, str | int | bool | float | list | dict) } # if it is a list we need to check if the contents are python types for key, value in params.items(): if isinstance(value, list): - params[key] = [item for item in value if isinstance(item, (str, int, bool, float, list, dict))] + params[key] = [item for item in value if isinstance(item, str | int | bool | float | list | dict)] return params diff --git a/src/backend/base/langflow/helpers/data.py b/src/backend/base/langflow/helpers/data.py index 381037078..4361789df 100644 --- a/src/backend/base/langflow/helpers/data.py +++ b/src/backend/base/langflow/helpers/data.py @@ -1,5 +1,3 @@ -from typing import Union - from langchain_core.documents import Document from langflow.schema import Data @@ -19,7 +17,7 @@ def docs_to_data(documents: list[Document]) -> list[Data]: return [Data.from_document(document) for document in documents] -def data_to_text(template: str, data: Union[Data, list[Data]], sep: str = "\n") -> str: +def data_to_text(template: str, data: Data | list[Data], sep: str = "\n") -> str: """ Converts a list of Data to a list of texts. @@ -43,7 +41,7 @@ def data_to_text(template: str, data: Union[Data, list[Data]], sep: str = "\n") return sep.join(formated_data) -def messages_to_text(template: str, messages: Union[Message, list[Message]]) -> str: +def messages_to_text(template: str, messages: Message | list[Message]) -> str: """ Converts a list of Messages to a list of texts. diff --git a/src/backend/base/langflow/helpers/flow.py b/src/backend/base/langflow/helpers/flow.py index 89530504d..57fced49d 100644 --- a/src/backend/base/langflow/helpers/flow.py +++ b/src/backend/base/langflow/helpers/flow.py @@ -1,4 +1,5 @@ -from typing import TYPE_CHECKING, Any, Awaitable, Callable, List, Optional, Tuple, Type, Union, cast +from collections.abc import Awaitable, Callable +from typing import TYPE_CHECKING, Any, cast from uuid import UUID from fastapi import HTTPException @@ -23,7 +24,7 @@ INPUT_TYPE_MAP = { } -def list_flows(*, user_id: Optional[str] = None) -> List[Data]: +def list_flows(*, user_id: str | None = None) -> list[Data]: if not user_id: raise ValueError("Session is invalid") try: @@ -39,7 +40,7 @@ def list_flows(*, user_id: Optional[str] = None) -> List[Data]: async def load_flow( - user_id: str, flow_id: Optional[str] = None, flow_name: Optional[str] = None, tweaks: Optional[dict] = None + user_id: str, flow_id: str | None = None, flow_name: str | None = None, tweaks: dict | None = None ) -> "Graph": from langflow.graph.graph.base import Graph from langflow.processing.process import process_tweaks @@ -61,21 +62,21 @@ async def load_flow( return graph -def find_flow(flow_name: str, user_id: str) -> Optional[str]: +def find_flow(flow_name: str, user_id: str) -> str | None: with session_scope() as session: flow = session.exec(select(Flow).where(Flow.name == flow_name).where(Flow.user_id == user_id)).first() return flow.id if flow else None async def run_flow( - inputs: Optional[Union[dict, List[dict]]] = None, - tweaks: Optional[dict] = None, - flow_id: Optional[str] = None, - flow_name: Optional[str] = None, - output_type: Optional[str] = "chat", - user_id: Optional[str] = None, - run_id: Optional[str] = None, -) -> List[RunOutputs]: + inputs: dict | list[dict] | None = None, + tweaks: dict | None = None, + flow_id: str | None = None, + flow_name: str | None = None, + output_type: str | None = "chat", + user_id: str | None = None, + run_id: str | None = None, +) -> list[RunOutputs]: if user_id is None: raise ValueError("Session is invalid") graph = await load_flow(user_id, flow_id, flow_name, tweaks) @@ -115,7 +116,7 @@ async def run_flow( def generate_function_for_flow( - inputs: List["Vertex"], flow_id: str, user_id: str | UUID | None + inputs: list["Vertex"], flow_id: str, user_id: str | UUID | None ) -> Callable[..., Awaitable[Any]]: """ Generate a dynamic flow function based on the given inputs and flow ID. @@ -189,7 +190,7 @@ async def flow_function({func_args}): def build_function_and_schema( flow_data: Data, graph: "Graph", user_id: str | UUID | None -) -> Tuple[Callable[..., Awaitable[Any]], Type[BaseModel]]: +) -> tuple[Callable[..., Awaitable[Any]], type[BaseModel]]: """ Builds a dynamic function and schema for a given flow. @@ -207,7 +208,7 @@ def build_function_and_schema( return dynamic_flow_function, schema -def get_flow_inputs(graph: "Graph") -> List["Vertex"]: +def get_flow_inputs(graph: "Graph") -> list["Vertex"]: """ Retrieves the flow inputs from the given graph. @@ -224,7 +225,7 @@ def get_flow_inputs(graph: "Graph") -> List["Vertex"]: return inputs -def build_schema_from_inputs(name: str, inputs: List["Vertex"]) -> Type[BaseModel]: +def build_schema_from_inputs(name: str, inputs: list["Vertex"]) -> type[BaseModel]: """ Builds a schema from the given inputs. @@ -245,7 +246,7 @@ def build_schema_from_inputs(name: str, inputs: List["Vertex"]) -> Type[BaseMode return create_model(name, **fields) # type: ignore -def get_arg_names(inputs: List["Vertex"]) -> List[dict[str, str]]: +def get_arg_names(inputs: list["Vertex"]) -> list[dict[str, str]]: """ Returns a list of dictionaries containing the component name and its corresponding argument name. @@ -261,7 +262,7 @@ def get_arg_names(inputs: List["Vertex"]) -> List[dict[str, str]]: ] -def get_flow_by_id_or_endpoint_name(flow_id_or_name: str, user_id: Optional[UUID] = None) -> FlowRead | None: +def get_flow_by_id_or_endpoint_name(flow_id_or_name: str, user_id: UUID | None = None) -> FlowRead | None: flow_read = None with session_scope() as session: endpoint_name = None diff --git a/src/backend/base/langflow/initial_setup/setup.py b/src/backend/base/langflow/initial_setup/setup.py index a1dd431ec..9a296e3f3 100644 --- a/src/backend/base/langflow/initial_setup/setup.py +++ b/src/backend/base/langflow/initial_setup/setup.py @@ -4,10 +4,10 @@ import os import shutil import time from collections import defaultdict +from collections.abc import Awaitable from copy import deepcopy from datetime import datetime, timezone from pathlib import Path -from typing import Awaitable from uuid import UUID import orjson @@ -343,7 +343,7 @@ def load_starter_projects(retries=3, delay=1) -> list[tuple[Path, dict]]: for file in folder.glob("*.json"): attempt = 0 while attempt < retries: - with open(file, "r", encoding="utf-8") as f: + with open(file, encoding="utf-8") as f: try: project = orjson.loads(f.read()) starter_projects.append((file, project)) @@ -510,7 +510,7 @@ def load_flows_from_directory(): if not filename.endswith(".json"): continue logger.info(f"Loading flow from file: {filename}") - with open(os.path.join(flows_path, filename), "r", encoding="utf-8") as file: + with open(os.path.join(flows_path, filename), encoding="utf-8") as file: flow = orjson.loads(file.read()) no_json_name = filename.replace(".json", "") flow_endpoint_name = flow.get("endpoint_name") diff --git a/src/backend/base/langflow/inputs/inputs.py b/src/backend/base/langflow/inputs/inputs.py index 21673761c..9b696155a 100644 --- a/src/backend/base/langflow/inputs/inputs.py +++ b/src/backend/base/langflow/inputs/inputs.py @@ -1,6 +1,6 @@ import warnings from collections.abc import AsyncIterator, Iterator -from typing import Any, Union, get_args +from typing import Any, get_args from pydantic import Field, field_validator @@ -38,7 +38,7 @@ class TableInput(BaseInputMixin, MetadataTraceMixin, TableMixin, ListableInputMi raise ValueError(f"TableInput value must be a list of dictionaries or Data. Value '{v}' is not a list.") for item in v: - if not isinstance(item, (dict, Data)): + if not isinstance(item, dict | Data): raise ValueError( f"TableInput value must be a list of dictionaries or Data. Item '{item}' is not a dictionary or Data." ) @@ -194,7 +194,7 @@ class MessageTextInput(StrInput, MetadataTraceMixin, InputTraceMixin): f"The input to '{input_name}' must contain the key '{v.text_key}'." f"You can set `text_key` to one of the following keys: {keys} or set the value using another Component." ) - elif isinstance(v, (AsyncIterator, Iterator)): + elif isinstance(v, AsyncIterator | Iterator): value = v else: raise ValueError(f"Invalid value type {type(v)}") @@ -276,7 +276,7 @@ class SecretStrInput(BaseInputMixin, DatabaseLoadMixin): f"The input to '{input_name}' must contain the key '{v.text_key}'." f"You can set `text_key` to one of the following keys: {keys} or set the value using another Component." ) - elif isinstance(v, (AsyncIterator, Iterator)): + elif isinstance(v, AsyncIterator | Iterator): value = v elif v is None: value = None @@ -315,7 +315,7 @@ class IntInput(BaseInputMixin, ListableInputMixin, RangeMixin, MetadataTraceMixi ValueError: If the value is not of a valid type or if the input is missing a required key. """ - if v and not isinstance(v, (int, float)): + if v and not isinstance(v, int | float): raise ValueError(f"Invalid value type {type(v)} for input {_info.data.get('name')}.") if isinstance(v, float): v = int(v) @@ -351,7 +351,7 @@ class FloatInput(BaseInputMixin, ListableInputMixin, RangeMixin, MetadataTraceMi Raises: ValueError: If the value is not of a valid type or if the input is missing a required key. """ - if v and not isinstance(v, (int, float)): + if v and not isinstance(v, int | float): raise ValueError(f"Invalid value type {type(v)} for input {_info.data.get('name')}.") if isinstance(v, int): v = float(v) @@ -485,30 +485,30 @@ class DefaultPromptField(Input): value: Any = "" # Set the value to empty string -InputTypes = Union[ - Input, - DefaultPromptField, - BoolInput, - DataInput, - DictInput, - DropdownInput, - MultiselectInput, - FileInput, - FloatInput, - HandleInput, - IntInput, - MultilineInput, - MultilineSecretInput, - NestedDictInput, - PromptInput, - CodeInput, - SecretStrInput, - StrInput, - MessageTextInput, - MessageInput, - TableInput, - LinkInput, -] +InputTypes = ( + Input + | DefaultPromptField + | BoolInput + | DataInput + | DictInput + | DropdownInput + | MultiselectInput + | FileInput + | FloatInput + | HandleInput + | IntInput + | MultilineInput + | MultilineSecretInput + | NestedDictInput + | PromptInput + | CodeInput + | SecretStrInput + | StrInput + | MessageTextInput + | MessageInput + | TableInput + | LinkInput +) InputTypesMap: dict[str, type[InputTypes]] = {t.__name__: t for t in get_args(InputTypes)} diff --git a/src/backend/base/langflow/interface/initialize/loading.py b/src/backend/base/langflow/interface/initialize/loading.py index 35a6e23b4..e5beee405 100644 --- a/src/backend/base/langflow/interface/initialize/loading.py +++ b/src/backend/base/langflow/interface/initialize/loading.py @@ -1,7 +1,9 @@ +from __future__ import annotations + import inspect import os import warnings -from typing import TYPE_CHECKING, Any, Type +from typing import TYPE_CHECKING, Any import orjson from loguru import logger @@ -19,7 +21,7 @@ if TYPE_CHECKING: async def instantiate_class( - vertex: "Vertex", + vertex: Vertex, user_id=None, event_manager: EventManager | None = None, ) -> Any: @@ -34,8 +36,8 @@ async def instantiate_class( custom_params = get_params(vertex.params) code = custom_params.pop("code") - class_object: Type["CustomComponent" | "Component"] = eval_custom_component_code(code) - custom_component: "CustomComponent" | "Component" = class_object( + class_object: type[CustomComponent | Component] = eval_custom_component_code(code) + custom_component: CustomComponent | Component = class_object( _user_id=user_id, _parameters=custom_params, _vertex=vertex, @@ -49,7 +51,7 @@ async def instantiate_class( async def get_instance_results( custom_component, custom_params: dict, - vertex: "Vertex", + vertex: Vertex, fallback_to_env_vars: bool = False, base_type: str = "component", ): @@ -101,7 +103,7 @@ def convert_kwargs(params): def update_params_with_load_from_db_fields( - custom_component: "CustomComponent", + custom_component: CustomComponent, params, load_from_db_fields, fallback_to_env_vars=False, @@ -144,7 +146,7 @@ def update_params_with_load_from_db_fields( async def build_component( params: dict, - custom_component: "Component", + custom_component: Component, ): # Now set the params as attributes of the custom_component custom_component.set_attributes(params) @@ -153,7 +155,7 @@ async def build_component( return custom_component, build_results, artifacts -async def build_custom_component(params: dict, custom_component: "CustomComponent"): +async def build_custom_component(params: dict, custom_component: CustomComponent): if "retriever" in params and hasattr(params["retriever"], "as_retriever"): params["retriever"] = params["retriever"].as_retriever() @@ -173,7 +175,7 @@ async def build_custom_component(params: dict, custom_component: "CustomComponen # Call the build method directly if it's sync build_result = custom_component.build(**params) custom_repr = custom_component.custom_repr() - if custom_repr is None and isinstance(build_result, (dict, Data, str)): + if custom_repr is None and isinstance(build_result, dict | Data | str): custom_repr = build_result if not isinstance(custom_repr, str): custom_repr = str(custom_repr) @@ -183,7 +185,7 @@ async def build_custom_component(params: dict, custom_component: "CustomComponen elif hasattr(raw, "model_dump") and raw is not None: raw = raw.model_dump() - if raw is None and isinstance(build_result, (dict, Data, str)): + if raw is None and isinstance(build_result, dict | Data | str): raw = build_result.data if isinstance(build_result, Data) else build_result artifact_type = get_artifact_type(custom_component.repr_value or raw, build_result) diff --git a/src/backend/base/langflow/interface/utils.py b/src/backend/base/langflow/interface/utils.py index dfee36021..e7f72cabc 100644 --- a/src/backend/base/langflow/interface/utils.py +++ b/src/backend/base/langflow/interface/utils.py @@ -18,7 +18,7 @@ def load_file_into_dict(file_path: str) -> dict: raise FileNotFoundError(f"File not found: {file_path}") # Files names are UUID, so we can't find the extension - with open(file_path, "r") as file: + with open(file_path) as file: try: data = json.load(file) except json.JSONDecodeError: diff --git a/src/backend/base/langflow/load/load.py b/src/backend/base/langflow/load/load.py index e569d331f..ebd7b5512 100644 --- a/src/backend/base/langflow/load/load.py +++ b/src/backend/base/langflow/load/load.py @@ -1,6 +1,5 @@ import json from pathlib import Path -from typing import List, Optional, Union from dotenv import load_dotenv from loguru import logger @@ -13,13 +12,13 @@ from langflow.utils.util import update_settings def load_flow_from_json( - flow: Union[Path, str, dict], - tweaks: Optional[dict] = None, - log_level: Optional[str] = None, - log_file: Optional[str] = None, - env_file: Optional[str] = None, - cache: Optional[str] = None, - disable_logs: Optional[bool] = True, + flow: Path | str | dict, + tweaks: dict | None = None, + log_level: str | None = None, + log_file: str | None = None, + env_file: str | None = None, + cache: str | None = None, + disable_logs: bool | None = True, ) -> Graph: """ Load a flow graph from a JSON file or a JSON object. @@ -53,8 +52,8 @@ def load_flow_from_json( # Update settings with cache and components path update_settings(cache=cache) - if isinstance(flow, (str, Path)): - with open(flow, "r", encoding="utf-8") as f: + if isinstance(flow, str | Path): + with open(flow, encoding="utf-8") as f: flow_graph = json.load(f) # If input is a dictionary, assume it's a JSON object elif isinstance(flow, dict): @@ -71,19 +70,19 @@ def load_flow_from_json( def run_flow_from_json( - flow: Union[Path, str, dict], + flow: Path | str | dict, input_value: str, - tweaks: Optional[dict] = None, + tweaks: dict | None = None, input_type: str = "chat", output_type: str = "chat", - output_component: Optional[str] = None, - log_level: Optional[str] = None, - log_file: Optional[str] = None, - env_file: Optional[str] = None, - cache: Optional[str] = None, - disable_logs: Optional[bool] = True, + output_component: str | None = None, + log_level: str | None = None, + log_file: str | None = None, + env_file: str | None = None, + cache: str | None = None, + disable_logs: bool | None = True, fallback_to_env_vars: bool = False, -) -> List[RunOutputs]: +) -> list[RunOutputs]: """ Run a flow from a JSON file or dictionary. diff --git a/src/backend/base/langflow/logging/logger.py b/src/backend/base/langflow/logging/logger.py index 01b146b68..381882b84 100644 --- a/src/backend/base/langflow/logging/logger.py +++ b/src/backend/base/langflow/logging/logger.py @@ -5,7 +5,7 @@ import sys from collections import deque from pathlib import Path from threading import Lock, Semaphore -from typing import Optional, TypedDict +from typing import TypedDict import orjson from loguru import logger @@ -144,10 +144,10 @@ class LogConfig(TypedDict): def configure( - log_level: Optional[str] = None, - log_file: Optional[Path] = None, - disable: Optional[bool] = False, - log_env: Optional[str] = None, + log_level: str | None = None, + log_file: Path | None = None, + disable: bool | None = False, + log_env: str | None = None, ): if disable and log_level is None and log_file is None: logger.disable("langflow") diff --git a/src/backend/base/langflow/main.py b/src/backend/base/langflow/main.py index d46fb31dd..8da3241a3 100644 --- a/src/backend/base/langflow/main.py +++ b/src/backend/base/langflow/main.py @@ -5,7 +5,6 @@ import warnings from contextlib import asynccontextmanager from http import HTTPStatus from pathlib import Path -from typing import Optional from urllib.parse import urlencode import nest_asyncio # type: ignore @@ -220,7 +219,7 @@ def get_static_files_dir(): return frontend_path / "frontend" -def setup_app(static_files_dir: Optional[Path] = None, backend_only: bool = False) -> FastAPI: +def setup_app(static_files_dir: Path | None = None, backend_only: bool = False) -> FastAPI: """Setup the FastAPI app.""" # get the directory of the current file logger.info(f"Setting up app with static files directory {static_files_dir}") diff --git a/src/backend/base/langflow/memory.py b/src/backend/base/langflow/memory.py index 3fd7c4a41..0af3b9075 100644 --- a/src/backend/base/langflow/memory.py +++ b/src/backend/base/langflow/memory.py @@ -1,5 +1,5 @@ import warnings -from typing import List, Sequence +from collections.abc import Sequence from uuid import UUID from langchain_core.messages import BaseMessage @@ -21,7 +21,7 @@ def get_messages( order: str | None = "DESC", flow_id: UUID | None = None, limit: int | None = None, -) -> List[Message]: +) -> list[Message]: """ Retrieves messages from the monitor service based on the provided filters. @@ -147,7 +147,7 @@ class LCBuiltinChatMemory(BaseChatMessageHistory): self.session_id = session_id @property - def messages(self) -> List[BaseMessage]: + def messages(self) -> list[BaseMessage]: messages = get_messages( session_id=self.session_id, ) diff --git a/src/backend/base/langflow/processing/base.py b/src/backend/base/langflow/processing/base.py index 26da99842..2ff2df9a3 100644 --- a/src/backend/base/langflow/processing/base.py +++ b/src/backend/base/langflow/processing/base.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, List, Union +from typing import TYPE_CHECKING, Union from langchain_core.callbacks import BaseCallbackHandler from loguru import logger @@ -34,7 +34,7 @@ def get_langfuse_callback(trace_id): return None -def flush_langfuse_callback_if_present(callbacks: List[Union[BaseCallbackHandler, "CallbackHandler"]]): +def flush_langfuse_callback_if_present(callbacks: list[Union[BaseCallbackHandler, "CallbackHandler"]]): """ If langfuse callback is present, run callback.langfuse.flush() """ diff --git a/src/backend/base/langflow/processing/process.py b/src/backend/base/langflow/processing/process.py index 3b4684ec6..a6a63b4ab 100644 --- a/src/backend/base/langflow/processing/process.py +++ b/src/backend/base/langflow/processing/process.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, cast +from typing import TYPE_CHECKING, Any, Union, cast from loguru import logger from pydantic import BaseModel @@ -23,10 +23,10 @@ async def run_graph_internal( graph: "Graph", flow_id: str, stream: bool = False, - session_id: Optional[str] = None, - inputs: Optional[List["InputValueRequest"]] = None, - outputs: Optional[List[str]] = None, -) -> tuple[List[RunOutputs], str]: + session_id: str | None = None, + inputs: list["InputValueRequest"] | None = None, + outputs: list[str] | None = None, +) -> tuple[list[RunOutputs], str]: """Run the graph and generate the result""" inputs = inputs or [] if session_id is None: @@ -64,8 +64,8 @@ def run_graph( input_type: str, output_type: str, fallback_to_env_vars: bool = False, - output_component: Optional[str] = None, -) -> List[RunOutputs]: + output_component: str | None = None, +) -> list[RunOutputs]: """ Runs the given Langflow Graph with the specified input and returns the outputs. @@ -113,8 +113,8 @@ def run_graph( def validate_input( - graph_data: Dict[str, Any], tweaks: Union["Tweaks", Dict[str, str | Dict[str, Any]]] -) -> List[Dict[str, Any]]: + graph_data: dict[str, Any], tweaks: Union["Tweaks", dict[str, str | dict[str, Any]]] +) -> list[dict[str, Any]]: if not isinstance(graph_data, dict) or not isinstance(tweaks, dict): raise ValueError("graph_data and tweaks should be dictionaries") @@ -126,7 +126,7 @@ def validate_input( return nodes -def apply_tweaks(node: Dict[str, Any], node_tweaks: Dict[str, Any]) -> None: +def apply_tweaks(node: dict[str, Any], node_tweaks: dict[str, Any]) -> None: template_data = node.get("data", {}).get("node", {}).get("template") if not isinstance(template_data, dict): @@ -146,15 +146,15 @@ def apply_tweaks(node: Dict[str, Any], node_tweaks: Dict[str, Any]) -> None: template_data[tweak_name][key] = tweak_value -def apply_tweaks_on_vertex(vertex: Vertex, node_tweaks: Dict[str, Any]) -> None: +def apply_tweaks_on_vertex(vertex: Vertex, node_tweaks: dict[str, Any]) -> None: for tweak_name, tweak_value in node_tweaks.items(): if tweak_name and tweak_value and tweak_name in vertex.params: vertex.params[tweak_name] = tweak_value def process_tweaks( - graph_data: Dict[str, Any], tweaks: Union["Tweaks", Dict[str, Dict[str, Any]]], stream: bool = False -) -> Dict[str, Any]: + graph_data: dict[str, Any], tweaks: Union["Tweaks", dict[str, dict[str, Any]]], stream: bool = False +) -> dict[str, Any]: """ This function is used to tweak the graph data using the node id and the tweaks dict. @@ -168,12 +168,12 @@ def process_tweaks( """ tweaks_dict = {} if not isinstance(tweaks, dict): - tweaks_dict = cast(Dict[str, Any], tweaks.model_dump()) + tweaks_dict = cast(dict[str, Any], tweaks.model_dump()) else: tweaks_dict = tweaks if "stream" not in tweaks_dict: tweaks_dict |= {"stream": stream} - nodes = validate_input(graph_data, cast(Dict[str, str | Dict[str, Any]], tweaks_dict)) + nodes = validate_input(graph_data, cast(dict[str, str | dict[str, Any]], tweaks_dict)) nodes_map = {node.get("id"): node for node in nodes} nodes_display_name_map = {node.get("data", {}).get("node", {}).get("display_name"): node for node in nodes} @@ -193,7 +193,7 @@ def process_tweaks( return graph_data -def process_tweaks_on_graph(graph: Graph, tweaks: Dict[str, Dict[str, Any]]): +def process_tweaks_on_graph(graph: Graph, tweaks: dict[str, dict[str, Any]]): for vertex in graph.vertices: if isinstance(vertex, Vertex) and isinstance(vertex.id, str): node_id = vertex.id diff --git a/src/backend/base/langflow/schema/artifact.py b/src/backend/base/langflow/schema/artifact.py index 27eb3c52c..ba408fba5 100644 --- a/src/backend/base/langflow/schema/artifact.py +++ b/src/backend/base/langflow/schema/artifact.py @@ -1,5 +1,5 @@ +from collections.abc import Generator from enum import Enum -from typing import Generator from fastapi.encoders import jsonable_encoder from pydantic import BaseModel @@ -64,7 +64,7 @@ def post_process_raw(raw, artifact_type: str): _raw.append(str(item)) raw = _raw elif artifact_type == ArtifactType.UNKNOWN.value and raw is not None: - if isinstance(raw, (BaseModel, dict)): + if isinstance(raw, BaseModel | dict): try: raw = jsonable_encoder(raw) artifact_type = ArtifactType.OBJECT.value diff --git a/src/backend/base/langflow/schema/data.py b/src/backend/base/langflow/schema/data.py index 4bc34e297..605a36275 100644 --- a/src/backend/base/langflow/schema/data.py +++ b/src/backend/base/langflow/schema/data.py @@ -1,6 +1,6 @@ import copy import json -from typing import Optional, cast +from typing import cast from langchain_core.documents import Document from langchain_core.messages import AIMessage, BaseMessage, HumanMessage @@ -21,7 +21,7 @@ class Data(BaseModel): text_key: str = "text" data: dict = {} - default_value: Optional[str] = "" + default_value: str | None = "" @model_validator(mode="before") @classmethod diff --git a/src/backend/base/langflow/schema/graph.py b/src/backend/base/langflow/schema/graph.py index 6b6cca66a..004d30de9 100644 --- a/src/backend/base/langflow/schema/graph.py +++ b/src/backend/base/langflow/schema/graph.py @@ -1,4 +1,4 @@ -from typing import Any, List, Optional, Union +from typing import Any from pydantic import BaseModel, Field, RootModel @@ -6,16 +6,16 @@ from langflow.schema.schema import InputType class InputValue(BaseModel): - components: Optional[List[str]] = [] - input_value: Optional[str] = None - type: Optional[InputType] = Field( + components: list[str] | None = [] + input_value: str | None = None + type: InputType | None = Field( "any", description="Defines on which components the input value should be applied. 'any' applies to all input components.", ) class Tweaks(RootModel): - root: dict[str, Union[str, dict[str, Any]]] = Field( + root: dict[str, str | dict[str, Any]] = Field( description="A dictionary of tweaks to adjust the flow's execution. Allows customizing flow behavior dynamically. All tweaks are overridden by the input values.", ) model_config = { diff --git a/src/backend/base/langflow/schema/image.py b/src/backend/base/langflow/schema/image.py index 552f75b8b..59c159c8e 100644 --- a/src/backend/base/langflow/schema/image.py +++ b/src/backend/base/langflow/schema/image.py @@ -13,7 +13,7 @@ def is_image_file(file_path): with PILImage.open(file_path) as img: img.verify() # Verify that it is, in fact, an image return True - except (IOError, SyntaxError): + except (OSError, SyntaxError): return False diff --git a/src/backend/base/langflow/schema/log.py b/src/backend/base/langflow/schema/log.py index 4cc17544e..72a6cb032 100644 --- a/src/backend/base/langflow/schema/log.py +++ b/src/backend/base/langflow/schema/log.py @@ -1,10 +1,10 @@ -from typing import Optional, Union +from typing import TypeAlias from pydantic import BaseModel from typing_extensions import Protocol -LoggableType = Union[str, dict, list, int, float, bool, None, BaseModel] +LoggableType: TypeAlias = str | dict | list | int | float | bool | None | BaseModel class LogFunctionType(Protocol): - def __call__(self, message: Union[LoggableType, list[LoggableType]], *, name: Optional[str] = None) -> None: ... + def __call__(self, message: LoggableType | list[LoggableType], *, name: str | None = None) -> None: ... diff --git a/src/backend/base/langflow/schema/message.py b/src/backend/base/langflow/schema/message.py index 53d7f0141..be7c49f09 100644 --- a/src/backend/base/langflow/schema/message.py +++ b/src/backend/base/langflow/schema/message.py @@ -1,6 +1,7 @@ import asyncio +from collections.abc import AsyncIterator, Iterator from datetime import datetime, timezone -from typing import Annotated, Any, AsyncIterator, Iterator, List, Optional +from typing import Annotated, Any from uuid import UUID from fastapi.encoders import jsonable_encoder @@ -38,15 +39,15 @@ class Message(Data): model_config = ConfigDict(arbitrary_types_allowed=True) # Helper class to deal with image data text_key: str = "text" - text: Optional[str | AsyncIterator | Iterator] = Field(default="") - sender: Optional[str] = None - sender_name: Optional[str] = None - files: Optional[list[str | Image]] = Field(default=[]) - session_id: Optional[str] = Field(default="") + text: str | AsyncIterator | Iterator | None = Field(default="") + sender: str | None = None + sender_name: str | None = None + files: list[str | Image] | None = Field(default=[]) + session_id: str | None = Field(default="") timestamp: Annotated[str, BeforeValidator(_timestamp_to_str)] = Field( default_factory=lambda: datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S") ) - flow_id: Optional[str | UUID] = None + flow_id: str | UUID | None = None @field_validator("flow_id", mode="before") @classmethod @@ -71,7 +72,7 @@ class Message(Data): return value def model_post_init(self, __context: Any) -> None: - new_files: List[Any] = [] + new_files: list[Any] = [] for file in self.files or []: if is_image_file(file): new_files.append(Image(path=file)) diff --git a/src/backend/base/langflow/schema/schema.py b/src/backend/base/langflow/schema/schema.py index ba8ab9b39..1d0ff2251 100644 --- a/src/backend/base/langflow/schema/schema.py +++ b/src/backend/base/langflow/schema/schema.py @@ -1,5 +1,6 @@ +from collections.abc import AsyncIterator, Generator, Iterator from enum import Enum -from typing import AsyncIterator, Generator, Iterator, Literal, Union +from typing import Literal from pydantic import BaseModel from typing_extensions import TypedDict @@ -33,7 +34,7 @@ class ErrorLog(TypedDict): class OutputValue(BaseModel): - message: Union[ErrorLog, StreamURL, dict, list, str] + message: ErrorLog | StreamURL | dict | list | str type: str @@ -73,7 +74,7 @@ def get_message(payload): elif hasattr(payload, "model_dump"): message = payload.model_dump() - if message is None and isinstance(payload, (dict, str, Data)): + if message is None and isinstance(payload, dict | str | Data): message = payload.data if isinstance(payload, Data) else payload return message or payload @@ -126,7 +127,7 @@ def recursive_serialize_or_str(obj): obj_dict = obj.dict() # type: ignore return {k: recursive_serialize_or_str(v) for k, v in obj_dict.items()} - elif isinstance(obj, (AsyncIterator, Generator, Iterator)): + elif isinstance(obj, AsyncIterator | Generator | Iterator): # contain memory addresses # without consuming the iterator # return list(obj) consumes the iterator diff --git a/src/backend/base/langflow/schema/table.py b/src/backend/base/langflow/schema/table.py index a26dd737f..2754406f4 100644 --- a/src/backend/base/langflow/schema/table.py +++ b/src/backend/base/langflow/schema/table.py @@ -1,5 +1,4 @@ from enum import Enum -from typing import List, Optional from pydantic import BaseModel, Field, field_validator @@ -16,7 +15,7 @@ class Column(BaseModel): name: str sortable: bool = Field(default=True) filterable: bool = Field(default=True) - formatter: Optional[FormatterType | str] = None + formatter: FormatterType | str | None = None @field_validator("formatter") def validate_formatter(cls, value): @@ -28,4 +27,4 @@ class Column(BaseModel): class TableSchema(BaseModel): - columns: List[Column] + columns: list[Column] diff --git a/src/backend/base/langflow/services/auth/utils.py b/src/backend/base/langflow/services/auth/utils.py index 61af0d1f2..ca963ade5 100644 --- a/src/backend/base/langflow/services/auth/utils.py +++ b/src/backend/base/langflow/services/auth/utils.py @@ -1,8 +1,9 @@ import base64 import random import warnings +from collections.abc import Coroutine from datetime import datetime, timedelta, timezone -from typing import Annotated, Coroutine, Optional, Union +from typing import Annotated from uuid import UUID from cryptography.fernet import Fernet @@ -32,9 +33,9 @@ async def api_key_security( query_param: str = Security(api_key_query), header_param: str = Security(api_key_header), db: Session = Depends(get_session), -) -> Optional[UserRead]: +) -> UserRead | None: settings_service = get_settings_service() - result: Optional[Union[ApiKey, User]] = None + result: ApiKey | User | None = None if settings_service.auth_settings.AUTO_LOGIN: # Get the first user if not settings_service.auth_settings.SUPERUSER: @@ -154,7 +155,7 @@ async def get_current_user_for_websocket( websocket: WebSocket, db: Session = Depends(get_session), query_param: str = Security(api_key_query), -) -> Optional[User]: +) -> User | None: token = websocket.query_params.get("token") api_key = websocket.query_params.get("x-api-key") if token: @@ -325,7 +326,7 @@ def create_refresh_token(refresh_token: str, db: Session = Depends(get_session)) ) from e -def authenticate_user(username: str, password: str, db: Session = Depends(get_session)) -> Optional[User]: +def authenticate_user(username: str, password: str, db: Session = Depends(get_session)) -> User | None: user = get_user_by_username(db, username) if not user: diff --git a/src/backend/base/langflow/services/cache/base.py b/src/backend/base/langflow/services/cache/base.py index 02d645183..845ea2db6 100644 --- a/src/backend/base/langflow/services/cache/base.py +++ b/src/backend/base/langflow/services/cache/base.py @@ -1,7 +1,7 @@ import abc import asyncio import threading -from typing import Generic, Optional, TypeVar +from typing import Generic, TypeVar from langflow.services.base import Service @@ -17,7 +17,7 @@ class CacheService(Service, Generic[LockType]): name = "cache_service" @abc.abstractmethod - def get(self, key, lock: Optional[LockType] = None): + def get(self, key, lock: LockType | None = None): """ Retrieve an item from the cache. @@ -29,7 +29,7 @@ class CacheService(Service, Generic[LockType]): """ @abc.abstractmethod - def set(self, key, value, lock: Optional[LockType] = None): + def set(self, key, value, lock: LockType | None = None): """ Add an item to the cache. @@ -39,7 +39,7 @@ class CacheService(Service, Generic[LockType]): """ @abc.abstractmethod - def upsert(self, key, value, lock: Optional[LockType] = None): + def upsert(self, key, value, lock: LockType | None = None): """ Add an item to the cache if it doesn't exist, or update it if it does. @@ -49,7 +49,7 @@ class CacheService(Service, Generic[LockType]): """ @abc.abstractmethod - def delete(self, key, lock: Optional[LockType] = None): + def delete(self, key, lock: LockType | None = None): """ Remove an item from the cache. @@ -58,7 +58,7 @@ class CacheService(Service, Generic[LockType]): """ @abc.abstractmethod - def clear(self, lock: Optional[LockType] = None): + def clear(self, lock: LockType | None = None): """ Clear all items from the cache. """ @@ -112,7 +112,7 @@ class AsyncBaseCacheService(Service, Generic[AsyncLockType]): name = "cache_service" @abc.abstractmethod - async def get(self, key, lock: Optional[AsyncLockType] = None): + async def get(self, key, lock: AsyncLockType | None = None): """ Retrieve an item from the cache. @@ -124,7 +124,7 @@ class AsyncBaseCacheService(Service, Generic[AsyncLockType]): """ @abc.abstractmethod - async def set(self, key, value, lock: Optional[AsyncLockType] = None): + async def set(self, key, value, lock: AsyncLockType | None = None): """ Add an item to the cache. @@ -134,7 +134,7 @@ class AsyncBaseCacheService(Service, Generic[AsyncLockType]): """ @abc.abstractmethod - async def upsert(self, key, value, lock: Optional[AsyncLockType] = None): + async def upsert(self, key, value, lock: AsyncLockType | None = None): """ Add an item to the cache if it doesn't exist, or update it if it does. @@ -144,7 +144,7 @@ class AsyncBaseCacheService(Service, Generic[AsyncLockType]): """ @abc.abstractmethod - async def delete(self, key, lock: Optional[AsyncLockType] = None): + async def delete(self, key, lock: AsyncLockType | None = None): """ Remove an item from the cache. @@ -153,7 +153,7 @@ class AsyncBaseCacheService(Service, Generic[AsyncLockType]): """ @abc.abstractmethod - async def clear(self, lock: Optional[AsyncLockType] = None): + async def clear(self, lock: AsyncLockType | None = None): """ Clear all items from the cache. """ diff --git a/src/backend/base/langflow/services/cache/disk.py b/src/backend/base/langflow/services/cache/disk.py index dbbd85f13..901dde2fe 100644 --- a/src/backend/base/langflow/services/cache/disk.py +++ b/src/backend/base/langflow/services/cache/disk.py @@ -1,7 +1,7 @@ import asyncio import pickle import time -from typing import Generic, Optional +from typing import Generic from diskcache import Cache from loguru import logger @@ -23,7 +23,7 @@ class AsyncDiskCache(AsyncBaseCacheService, Generic[AsyncLockType]): # type: ig self.max_size = max_size self.expiration_time = expiration_time - async def get(self, key, lock: Optional[asyncio.Lock] = None): + async def get(self, key, lock: asyncio.Lock | None = None): if not lock: async with self.lock: return await self._get(key) @@ -41,7 +41,7 @@ class AsyncDiskCache(AsyncBaseCacheService, Generic[AsyncLockType]): # type: ig await self._delete(key) # Log before deleting the expired item return CACHE_MISS - async def set(self, key, value, lock: Optional[asyncio.Lock] = None): + async def set(self, key, value, lock: asyncio.Lock | None = None): if not lock: async with self.lock: await self._set(key, value) @@ -51,10 +51,10 @@ class AsyncDiskCache(AsyncBaseCacheService, Generic[AsyncLockType]): # type: ig async def _set(self, key, value): if self.max_size and len(self.cache) >= self.max_size: await asyncio.to_thread(self.cache.cull) - item = {"value": pickle.dumps(value) if not isinstance(value, (str, bytes)) else value, "time": time.time()} + item = {"value": pickle.dumps(value) if not isinstance(value, str | bytes) else value, "time": time.time()} await asyncio.to_thread(self.cache.set, key, item) - async def delete(self, key, lock: Optional[asyncio.Lock] = None): + async def delete(self, key, lock: asyncio.Lock | None = None): if not lock: async with self.lock: await self._delete(key) @@ -64,7 +64,7 @@ class AsyncDiskCache(AsyncBaseCacheService, Generic[AsyncLockType]): # type: ig async def _delete(self, key): await asyncio.to_thread(self.cache.delete, key) - async def clear(self, lock: Optional[asyncio.Lock] = None): + async def clear(self, lock: asyncio.Lock | None = None): if not lock: async with self.lock: await self._clear() @@ -74,7 +74,7 @@ class AsyncDiskCache(AsyncBaseCacheService, Generic[AsyncLockType]): # type: ig async def _clear(self): await asyncio.to_thread(self.cache.clear) - async def upsert(self, key, value, lock: Optional[asyncio.Lock] = None): + async def upsert(self, key, value, lock: asyncio.Lock | None = None): if not lock: async with self.lock: await self._upsert(key, value) diff --git a/src/backend/base/langflow/services/cache/service.py b/src/backend/base/langflow/services/cache/service.py index 021c33f90..2511b76dc 100644 --- a/src/backend/base/langflow/services/cache/service.py +++ b/src/backend/base/langflow/services/cache/service.py @@ -3,7 +3,7 @@ import pickle import threading import time from collections import OrderedDict -from typing import Generic, Optional +from typing import Generic, Union from loguru import logger @@ -50,7 +50,7 @@ class ThreadingInMemoryCache(CacheService, Generic[LockType]): # type: ignore self.max_size = max_size self.expiration_time = expiration_time - def get(self, key, lock: Optional[threading.Lock] = None): + def get(self, key, lock: Union[threading.Lock, None] = None): # noqa: UP007 """ Retrieve an item from the cache. @@ -81,7 +81,7 @@ class ThreadingInMemoryCache(CacheService, Generic[LockType]): # type: ignore self.delete(key) return None - def set(self, key, value, lock: Optional[threading.Lock] = None): + def set(self, key, value, lock: Union[threading.Lock, None] = None): # noqa: UP007 """ Add an item to the cache. @@ -102,7 +102,7 @@ class ThreadingInMemoryCache(CacheService, Generic[LockType]): # type: ignore self._cache[key] = {"value": value, "time": time.time()} - def upsert(self, key, value, lock: Optional[threading.Lock] = None): + def upsert(self, key, value, lock: Union[threading.Lock, None] = None): # noqa: UP007 """ Inserts or updates a value in the cache. If the existing value and the new value are both dictionaries, they are merged. @@ -119,7 +119,7 @@ class ThreadingInMemoryCache(CacheService, Generic[LockType]): # type: ignore self.set(key, value) - def get_or_set(self, key, value, lock: Optional[threading.Lock] = None): + def get_or_set(self, key, value, lock: Union[threading.Lock, None] = None): # noqa: UP007 """ Retrieve an item from the cache. If the item does not exist, set it with the provided value. @@ -137,7 +137,7 @@ class ThreadingInMemoryCache(CacheService, Generic[LockType]): # type: ignore self.set(key, value) return value - def delete(self, key, lock: Optional[threading.Lock] = None): + def delete(self, key, lock: Union[threading.Lock, None] = None): # noqa: UP007 """ Remove an item from the cache. @@ -147,7 +147,7 @@ class ThreadingInMemoryCache(CacheService, Generic[LockType]): # type: ignore with lock or self._lock: self._cache.pop(key, None) - def clear(self, lock: Optional[threading.Lock] = None): + def clear(self, lock: Union[threading.Lock, None] = None): # noqa: UP007 """ Clear all items from the cache. """ @@ -337,7 +337,7 @@ class AsyncInMemoryCache(AsyncBaseCacheService, Generic[AsyncLockType]): # type self.max_size = max_size self.expiration_time = expiration_time - async def get(self, key, lock: Optional[asyncio.Lock] = None): + async def get(self, key, lock: asyncio.Lock | None = None): if not lock: async with self.lock: return await self._get(key) @@ -355,7 +355,7 @@ class AsyncInMemoryCache(AsyncBaseCacheService, Generic[AsyncLockType]): # type await self._delete(key) # Log before deleting the expired item return CACHE_MISS - async def set(self, key, value, lock: Optional[asyncio.Lock] = None): + async def set(self, key, value, lock: asyncio.Lock | None = None): if not lock: async with self.lock: await self._set( @@ -374,7 +374,7 @@ class AsyncInMemoryCache(AsyncBaseCacheService, Generic[AsyncLockType]): # type self.cache[key] = {"value": value, "time": time.time()} self.cache.move_to_end(key) - async def delete(self, key, lock: Optional[asyncio.Lock] = None): + async def delete(self, key, lock: asyncio.Lock | None = None): if not lock: async with self.lock: await self._delete(key) @@ -385,7 +385,7 @@ class AsyncInMemoryCache(AsyncBaseCacheService, Generic[AsyncLockType]): # type if key in self.cache: del self.cache[key] - async def clear(self, lock: Optional[asyncio.Lock] = None): + async def clear(self, lock: asyncio.Lock | None = None): if not lock: async with self.lock: await self._clear() @@ -395,7 +395,7 @@ class AsyncInMemoryCache(AsyncBaseCacheService, Generic[AsyncLockType]): # type async def _clear(self): self.cache.clear() - async def upsert(self, key, value, lock: Optional[asyncio.Lock] = None): + async def upsert(self, key, value, lock: asyncio.Lock | None = None): if not lock: async with self.lock: await self._upsert(key, value) diff --git a/src/backend/base/langflow/services/cache/utils.py b/src/backend/base/langflow/services/cache/utils.py index c2f3c9611..acff20f28 100644 --- a/src/backend/base/langflow/services/cache/utils.py +++ b/src/backend/base/langflow/services/cache/utils.py @@ -4,7 +4,7 @@ import hashlib import os import tempfile from pathlib import Path -from typing import TYPE_CHECKING, Any, Dict +from typing import TYPE_CHECKING, Any from fastapi import UploadFile from platformdirs import user_cache_dir @@ -12,7 +12,7 @@ from platformdirs import user_cache_dir if TYPE_CHECKING: from langflow.api.v1.schemas import BuildStatus -CACHE: Dict[str, Any] = {} +CACHE: dict[str, Any] = {} CACHE_DIR = user_cache_dir("langflow", "langflow") diff --git a/src/backend/base/langflow/services/chat/cache.py b/src/backend/base/langflow/services/chat/cache.py index 959ea877c..83de27ae9 100644 --- a/src/backend/base/langflow/services/chat/cache.py +++ b/src/backend/base/langflow/services/chat/cache.py @@ -1,5 +1,6 @@ +from collections.abc import Awaitable, Callable from contextlib import contextmanager -from typing import Any, Awaitable, Callable, List, Optional +from typing import Any import pandas as pd from PIL import Image @@ -11,7 +12,7 @@ class Subject: """Base class for implementing the observer pattern.""" def __init__(self): - self.observers: List[Callable[[], None]] = [] + self.observers: list[Callable[[], None]] = [] def attach(self, observer: Callable[[], None]): """Attach an observer to the subject.""" @@ -33,7 +34,7 @@ class AsyncSubject: """Base class for implementing the async observer pattern.""" def __init__(self): - self.observers: List[Callable[[], Awaitable]] = [] + self.observers: list[Callable[[], Awaitable]] = [] def attach(self, observer: Callable[[], Awaitable]): """Attach an observer to the subject.""" @@ -79,7 +80,7 @@ class CacheService(Subject, Service): self.current_client_id = previous_client_id self.current_cache = self._cache.get(self.current_client_id, {}) - def add(self, name: str, obj: Any, obj_type: str, extension: Optional[str] = None): + def add(self, name: str, obj: Any, obj_type: str, extension: str | None = None): """ Add an object to the current client's cache. @@ -111,7 +112,7 @@ class CacheService(Subject, Service): name (str): The cache key. obj (Any): The pandas DataFrame or Series object. """ - if isinstance(obj, (pd.DataFrame, pd.Series)): + if isinstance(obj, pd.DataFrame | pd.Series): self.add(name, obj.to_csv(), "pandas", extension="csv") else: raise ValueError("Object is not a pandas DataFrame or Series") diff --git a/src/backend/base/langflow/services/chat/service.py b/src/backend/base/langflow/services/chat/service.py index abf57e4e3..7522219af 100644 --- a/src/backend/base/langflow/services/chat/service.py +++ b/src/backend/base/langflow/services/chat/service.py @@ -1,7 +1,7 @@ import asyncio from collections import defaultdict from threading import RLock -from typing import Any, Optional +from typing import Any from langflow.services.base import Service from langflow.services.cache.base import AsyncBaseCacheService @@ -36,7 +36,7 @@ class ChatService(Service): return self._sync_cache_locks[key] async def _perform_cache_operation( - self, operation: str, key: str, data: Any = None, lock: Optional[asyncio.Lock] = None + self, operation: str, key: str, data: Any = None, lock: asyncio.Lock | None = None ): """ Perform a cache operation based on the given operation type. @@ -70,7 +70,7 @@ class ChatService(Service): elif operation == "delete": self.cache_service.delete(key, lock=lock) - async def set_cache(self, key: str, data: Any, lock: Optional[asyncio.Lock] = None) -> bool: + async def set_cache(self, key: str, data: Any, lock: asyncio.Lock | None = None) -> bool: """ Set the cache for a client. @@ -89,7 +89,7 @@ class ChatService(Service): await self._perform_cache_operation("upsert", key, result_dict, lock) return key in self.cache_service - async def get_cache(self, key: str, lock: Optional[asyncio.Lock] = None) -> Any: + async def get_cache(self, key: str, lock: asyncio.Lock | None = None) -> Any: """ Get the cache for a client. @@ -102,7 +102,7 @@ class ChatService(Service): """ return await self._perform_cache_operation("get", key, lock=lock or self._get_lock(key)) - async def clear_cache(self, key: str, lock: Optional[asyncio.Lock] = None): + async def clear_cache(self, key: str, lock: asyncio.Lock | None = None): """ Clear the cache for a client. diff --git a/src/backend/base/langflow/services/database/models/api_key/crud.py b/src/backend/base/langflow/services/database/models/api_key/crud.py index 37fe08cd1..6fa310373 100644 --- a/src/backend/base/langflow/services/database/models/api_key/crud.py +++ b/src/backend/base/langflow/services/database/models/api_key/crud.py @@ -1,7 +1,6 @@ import datetime import secrets import threading -from typing import List, Optional from uuid import UUID from sqlmodel import Session, select @@ -10,7 +9,7 @@ from sqlmodel.sql.expression import SelectOfScalar from langflow.services.database.models.api_key import ApiKey, ApiKeyCreate, ApiKeyRead, UnmaskedApiKeyRead -def get_api_keys(session: Session, user_id: UUID) -> List[ApiKeyRead]: +def get_api_keys(session: Session, user_id: UUID) -> list[ApiKeyRead]: query: SelectOfScalar = select(ApiKey).where(ApiKey.user_id == user_id) api_keys = session.exec(query).all() return [ApiKeyRead.model_validate(api_key) for api_key in api_keys] @@ -43,10 +42,10 @@ def delete_api_key(session: Session, api_key_id: UUID) -> None: session.commit() -def check_key(session: Session, api_key: str) -> Optional[ApiKey]: +def check_key(session: Session, api_key: str) -> ApiKey | None: """Check if the API key is valid.""" query: SelectOfScalar = select(ApiKey).where(ApiKey.api_key == api_key) - api_key_object: Optional[ApiKey] = session.exec(query).first() + api_key_object: ApiKey | None = session.exec(query).first() if api_key_object is not None: threading.Thread( target=update_total_uses, diff --git a/src/backend/base/langflow/services/database/models/api_key/model.py b/src/backend/base/langflow/services/database/models/api_key/model.py index 1a0baaef5..fa1ef8647 100644 --- a/src/backend/base/langflow/services/database/models/api_key/model.py +++ b/src/backend/base/langflow/services/database/models/api_key/model.py @@ -1,5 +1,5 @@ from datetime import datetime, timezone -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from uuid import UUID, uuid4 from pydantic import field_validator @@ -14,15 +14,15 @@ def utc_now(): class ApiKeyBase(SQLModel): - name: Optional[str] = Field(index=True, nullable=True, default=None) - last_used_at: Optional[datetime] = Field(default=None, nullable=True) + name: str | None = Field(index=True, nullable=True, default=None) + last_used_at: datetime | None = Field(default=None, nullable=True) total_uses: int = Field(default=0) is_active: bool = Field(default=True) class ApiKey(ApiKeyBase, table=True): # type: ignore id: UUID = Field(default_factory=uuid4, primary_key=True, unique=True) - created_at: Optional[datetime] = Field( + created_at: datetime | None = Field( default=None, sa_column=Column(DateTime(timezone=True), server_default=func.now(), nullable=False) ) api_key: str = Field(index=True, unique=True) @@ -35,9 +35,9 @@ class ApiKey(ApiKeyBase, table=True): # type: ignore class ApiKeyCreate(ApiKeyBase): - api_key: Optional[str] = None - user_id: Optional[UUID] = None - created_at: Optional[datetime] = Field(default_factory=utc_now) + api_key: str | None = None + user_id: UUID | None = None + created_at: datetime | None = Field(default_factory=utc_now) @field_validator("created_at", mode="before") @classmethod 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 0a09bbc18..26bb80e31 100644 --- a/src/backend/base/langflow/services/database/models/flow/model.py +++ b/src/backend/base/langflow/services/database/models/flow/model.py @@ -3,7 +3,7 @@ import re import warnings from datetime import datetime, timezone -from typing import TYPE_CHECKING, Dict, List, Optional +from typing import TYPE_CHECKING, Optional from uuid import UUID, uuid4 import emoji @@ -25,14 +25,14 @@ if TYPE_CHECKING: class FlowBase(SQLModel): name: str = Field(index=True) - description: Optional[str] = Field(default=None, sa_column=Column(Text, index=True, nullable=True)) - icon: Optional[str] = Field(default=None, nullable=True) - icon_bg_color: Optional[str] = Field(default=None, nullable=True) - data: Optional[Dict] = Field(default=None, nullable=True) - is_component: Optional[bool] = Field(default=False, nullable=True) - updated_at: Optional[datetime] = Field(default_factory=lambda: datetime.now(timezone.utc), nullable=True) - webhook: Optional[bool] = Field(default=False, nullable=True, description="Can be used on the webhook endpoint") - endpoint_name: Optional[str] = Field(default=None, nullable=True, index=True) + description: str | None = Field(default=None, sa_column=Column(Text, index=True, nullable=True)) + icon: str | None = Field(default=None, nullable=True) + icon_bg_color: str | None = Field(default=None, nullable=True) + data: dict | None = Field(default=None, nullable=True) + is_component: bool | None = Field(default=False, nullable=True) + updated_at: datetime | None = Field(default_factory=lambda: datetime.now(timezone.utc), nullable=True) + webhook: bool | None = Field(default=False, nullable=True, description="Can be used on the webhook endpoint") + endpoint_name: str | None = Field(default=None, nullable=True, index=True) @field_validator("endpoint_name") @classmethod @@ -139,14 +139,14 @@ class FlowBase(SQLModel): class Flow(FlowBase, table=True): # type: ignore id: UUID = Field(default_factory=uuid4, primary_key=True, unique=True) - data: Optional[Dict] = Field(default=None, sa_column=Column(JSON)) - user_id: Optional[UUID] = Field(index=True, foreign_key="user.id", nullable=True) + data: dict | None = Field(default=None, sa_column=Column(JSON)) + user_id: UUID | None = Field(index=True, foreign_key="user.id", nullable=True) user: "User" = Relationship(back_populates="flows") - folder_id: Optional[UUID] = Field(default=None, foreign_key="folder.id", nullable=True, index=True) + folder_id: UUID | None = Field(default=None, foreign_key="folder.id", nullable=True, index=True) folder: Optional["Folder"] = Relationship(back_populates="flows") - messages: List["MessageTable"] = Relationship(back_populates="flow") - transactions: List["TransactionTable"] = Relationship(back_populates="flow") - vertex_builds: List["VertexBuildTable"] = Relationship(back_populates="flow") + messages: list["MessageTable"] = Relationship(back_populates="flow") + transactions: list["TransactionTable"] = Relationship(back_populates="flow") + vertex_builds: list["VertexBuildTable"] = Relationship(back_populates="flow") def to_data(self): serialized = self.model_dump() @@ -167,22 +167,22 @@ class Flow(FlowBase, table=True): # type: ignore class FlowCreate(FlowBase): - user_id: Optional[UUID] = None - folder_id: Optional[UUID] = None + user_id: UUID | None = None + folder_id: UUID | None = None class FlowRead(FlowBase): id: UUID - user_id: Optional[UUID] = Field() - folder_id: Optional[UUID] = Field() + user_id: UUID | None = Field() + folder_id: UUID | None = Field() class FlowUpdate(SQLModel): - name: Optional[str] = None - description: Optional[str] = None - data: Optional[Dict] = None - folder_id: Optional[UUID] = None - endpoint_name: Optional[str] = None + name: str | None = None + description: str | None = None + data: dict | None = None + folder_id: UUID | None = None + endpoint_name: str | None = None @field_validator("endpoint_name") @classmethod diff --git a/src/backend/base/langflow/services/database/models/flow/utils.py b/src/backend/base/langflow/services/database/models/flow/utils.py index 1eb0db7aa..4bfe246e7 100644 --- a/src/backend/base/langflow/services/database/models/flow/utils.py +++ b/src/backend/base/langflow/services/database/models/flow/utils.py @@ -1,5 +1,3 @@ -from typing import Optional - from fastapi import Depends from sqlmodel import Session @@ -9,7 +7,7 @@ from langflow.utils.version import get_version_info from .model import Flow -def get_flow_by_id(session: Session = Depends(get_session), flow_id: Optional[str] = None) -> Flow | None: +def get_flow_by_id(session: Session = Depends(get_session), flow_id: str | None = None) -> Flow | None: """Get flow by id.""" if flow_id is None: diff --git a/src/backend/base/langflow/services/database/models/folder/model.py b/src/backend/base/langflow/services/database/models/folder/model.py index fa8570ae3..d57899a44 100644 --- a/src/backend/base/langflow/services/database/models/folder/model.py +++ b/src/backend/base/langflow/services/database/models/folder/model.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING, Optional from uuid import UUID, uuid4 from sqlalchemy import Text, UniqueConstraint @@ -13,21 +13,21 @@ if TYPE_CHECKING: class FolderBase(SQLModel): name: str = Field(index=True) - description: Optional[str] = Field(default=None, sa_column=Column(Text)) + description: str | None = Field(default=None, sa_column=Column(Text)) class Folder(FolderBase, table=True): # type: ignore - id: Optional[UUID] = Field(default_factory=uuid4, primary_key=True) - parent_id: Optional[UUID] = Field(default=None, foreign_key="folder.id") + id: UUID | None = Field(default_factory=uuid4, primary_key=True) + parent_id: UUID | None = Field(default=None, foreign_key="folder.id") parent: Optional["Folder"] = Relationship( back_populates="children", sa_relationship_kwargs=dict(remote_side="Folder.id"), ) - children: List["Folder"] = Relationship(back_populates="parent") - user_id: Optional[UUID] = Field(default=None, foreign_key="user.id") + children: list["Folder"] = Relationship(back_populates="parent") + user_id: UUID | None = Field(default=None, foreign_key="user.id") user: "User" = Relationship(back_populates="folders") - flows: List["Flow"] = Relationship( + flows: list["Flow"] = Relationship( back_populates="folder", sa_relationship_kwargs={"cascade": "all, delete, delete-orphan"} ) @@ -35,24 +35,24 @@ class Folder(FolderBase, table=True): # type: ignore class FolderCreate(FolderBase): - components_list: Optional[List[UUID]] = None - flows_list: Optional[List[UUID]] = None + components_list: list[UUID] | None = None + flows_list: list[UUID] | None = None class FolderRead(FolderBase): id: UUID - parent_id: Optional[UUID] = Field() + parent_id: UUID | None = Field() class FolderReadWithFlows(FolderBase): id: UUID - parent_id: Optional[UUID] = Field() - flows: List["FlowRead"] = Field(default=[]) + parent_id: UUID | None = Field() + flows: list["FlowRead"] = Field(default=[]) class FolderUpdate(SQLModel): - name: Optional[str] = None - description: Optional[str] = None - parent_id: Optional[UUID] = None - components: List[UUID] = Field(default_factory=list) - flows: List[UUID] = Field(default_factory=list) + name: str | None = None + description: str | None = None + parent_id: UUID | None = None + components: list[UUID] = Field(default_factory=list) + flows: list[UUID] = Field(default_factory=list) diff --git a/src/backend/base/langflow/services/database/models/message/model.py b/src/backend/base/langflow/services/database/models/message/model.py index 79d547606..d6e027885 100644 --- a/src/backend/base/langflow/services/database/models/message/model.py +++ b/src/backend/base/langflow/services/database/models/message/model.py @@ -1,5 +1,5 @@ from datetime import datetime, timezone -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING from uuid import UUID, uuid4 from pydantic import field_validator @@ -63,9 +63,9 @@ class MessageBase(SQLModel): class MessageTable(MessageBase, table=True): # type: ignore __tablename__ = "message" id: UUID = Field(default_factory=uuid4, primary_key=True) - flow_id: Optional[UUID] = Field(default=None, foreign_key="flow.id") + flow_id: UUID | None = Field(default=None, foreign_key="flow.id") flow: "Flow" = Relationship(back_populates="messages") - files: List[str] = Field(sa_column=Column(JSON)) + files: list[str] = Field(sa_column=Column(JSON)) @field_validator("flow_id", mode="before") @classmethod @@ -83,7 +83,7 @@ class MessageTable(MessageBase, table=True): # type: ignore class MessageRead(MessageBase): id: UUID - flow_id: Optional[UUID] = Field() + flow_id: UUID | None = Field() class MessageCreate(MessageBase): @@ -91,8 +91,8 @@ class MessageCreate(MessageBase): class MessageUpdate(SQLModel): - text: Optional[str] = None - sender: Optional[str] = None - sender_name: Optional[str] = None - session_id: Optional[str] = None - files: Optional[list[str]] = None + text: str | None = None + sender: str | None = None + sender_name: str | None = None + session_id: str | None = None + files: list[str] | None = None diff --git a/src/backend/base/langflow/services/database/models/transactions/crud.py b/src/backend/base/langflow/services/database/models/transactions/crud.py index 689e3db24..256f91400 100644 --- a/src/backend/base/langflow/services/database/models/transactions/crud.py +++ b/src/backend/base/langflow/services/database/models/transactions/crud.py @@ -1,4 +1,3 @@ -from typing import Optional from uuid import UUID from sqlalchemy.exc import IntegrityError @@ -7,7 +6,7 @@ from sqlmodel import Session, col, select from langflow.services.database.models.transactions.model import TransactionBase, TransactionTable -def get_transactions_by_flow_id(db: Session, flow_id: UUID, limit: Optional[int] = 1000) -> list[TransactionTable]: +def get_transactions_by_flow_id(db: Session, flow_id: UUID, limit: int | None = 1000) -> list[TransactionTable]: stmt = ( select(TransactionTable) .where(TransactionTable.flow_id == flow_id) diff --git a/src/backend/base/langflow/services/database/models/transactions/model.py b/src/backend/base/langflow/services/database/models/transactions/model.py index 2b8978d80..effcf36cb 100644 --- a/src/backend/base/langflow/services/database/models/transactions/model.py +++ b/src/backend/base/langflow/services/database/models/transactions/model.py @@ -1,5 +1,5 @@ from datetime import datetime, timezone -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from uuid import UUID, uuid4 from pydantic import field_validator @@ -12,11 +12,11 @@ if TYPE_CHECKING: class TransactionBase(SQLModel): timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) vertex_id: str = Field(nullable=False) - target_id: Optional[str] = Field(default=None) - inputs: Optional[dict] = Field(default=None, sa_column=Column(JSON)) - outputs: Optional[dict] = Field(default=None, sa_column=Column(JSON)) + target_id: str | None = Field(default=None) + inputs: dict | None = Field(default=None, sa_column=Column(JSON)) + outputs: dict | None = Field(default=None, sa_column=Column(JSON)) status: str = Field(nullable=False) - error: Optional[str] = Field(default=None) + error: str | None = Field(default=None) flow_id: UUID = Field(foreign_key="flow.id") # Needed for Column(JSON) @@ -35,7 +35,7 @@ class TransactionBase(SQLModel): class TransactionTable(TransactionBase, table=True): # type: ignore __tablename__ = "transaction" - id: Optional[UUID] = Field(default_factory=uuid4, primary_key=True) + id: UUID | None = Field(default_factory=uuid4, primary_key=True) flow: "Flow" = Relationship(back_populates="transactions") diff --git a/src/backend/base/langflow/services/database/models/user/crud.py b/src/backend/base/langflow/services/database/models/user/crud.py index 5a948815e..f329d4667 100644 --- a/src/backend/base/langflow/services/database/models/user/crud.py +++ b/src/backend/base/langflow/services/database/models/user/crud.py @@ -1,5 +1,4 @@ from datetime import datetime, timezone -from typing import Optional, Union from uuid import UUID from fastapi import Depends, HTTPException, status @@ -11,15 +10,15 @@ from langflow.services.database.models.user.model import User, UserUpdate from langflow.services.deps import get_session -def get_user_by_username(db: Session, username: str) -> Union[User, None]: +def get_user_by_username(db: Session, username: str) -> User | None: return db.exec(select(User).where(User.username == username)).first() -def get_user_by_id(db: Session, id: UUID) -> Union[User, None]: +def get_user_by_id(db: Session, id: UUID) -> User | None: return db.exec(select(User).where(User.id == id)).first() -def update_user(user_db: Optional[User], user: UserUpdate, db: Session = Depends(get_session)) -> User: +def update_user(user_db: User | None, user: UserUpdate, db: Session = Depends(get_session)) -> User: if not user_db: raise HTTPException(status_code=404, detail="User not found") diff --git a/src/backend/base/langflow/services/database/models/user/model.py b/src/backend/base/langflow/services/database/models/user/model.py index cf072b184..fc3e2e7f9 100644 --- a/src/backend/base/langflow/services/database/models/user/model.py +++ b/src/backend/base/langflow/services/database/models/user/model.py @@ -1,5 +1,5 @@ from datetime import datetime, timezone -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from uuid import UUID, uuid4 from sqlmodel import Field, Relationship, SQLModel @@ -15,17 +15,17 @@ class User(SQLModel, table=True): # type: ignore id: UUID = Field(default_factory=uuid4, primary_key=True, unique=True) username: str = Field(index=True, unique=True) password: str = Field() - profile_image: Optional[str] = Field(default=None, nullable=True) + profile_image: str | None = Field(default=None, nullable=True) is_active: bool = Field(default=False) is_superuser: bool = Field(default=False) create_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) - last_login_at: Optional[datetime] = Field(default=None, nullable=True) + last_login_at: datetime | None = Field(default=None, nullable=True) api_keys: list["ApiKey"] = Relationship( back_populates="user", sa_relationship_kwargs={"cascade": "delete"}, ) - store_api_key: Optional[str] = Field(default=None, nullable=True) + store_api_key: str | None = Field(default=None, nullable=True) flows: list["Flow"] = Relationship(back_populates="user") variables: list["Variable"] = Relationship( back_populates="user", @@ -45,18 +45,18 @@ class UserCreate(SQLModel): class UserRead(SQLModel): id: UUID = Field(default_factory=uuid4) username: str = Field() - profile_image: Optional[str] = Field() + profile_image: str | None = Field() is_active: bool = Field() is_superuser: bool = Field() create_at: datetime = Field() updated_at: datetime = Field() - last_login_at: Optional[datetime] = Field(nullable=True) + last_login_at: datetime | None = Field(nullable=True) class UserUpdate(SQLModel): - username: Optional[str] = None - profile_image: Optional[str] = None - password: Optional[str] = None - is_active: Optional[bool] = None - is_superuser: Optional[bool] = None - last_login_at: Optional[datetime] = None + username: str | None = None + profile_image: str | None = None + password: str | None = None + is_active: bool | None = None + is_superuser: bool | None = None + last_login_at: datetime | None = None diff --git a/src/backend/base/langflow/services/database/models/variable/model.py b/src/backend/base/langflow/services/database/models/variable/model.py index fa1a0b264..19cd04866 100644 --- a/src/backend/base/langflow/services/database/models/variable/model.py +++ b/src/backend/base/langflow/services/database/models/variable/model.py @@ -1,5 +1,5 @@ from datetime import datetime, timezone -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING from uuid import UUID, uuid4 from pydantic import ValidationInfo, field_validator @@ -18,45 +18,45 @@ def utc_now(): class VariableBase(SQLModel): name: str = Field(description="Name of the variable") value: str = Field(description="Encrypted value of the variable") - default_fields: Optional[List[str]] = Field(sa_column=Column(JSON)) - type: Optional[str] = Field(None, description="Type of the variable") + default_fields: list[str] | None = Field(sa_column=Column(JSON)) + type: str | None = Field(None, description="Type of the variable") class Variable(VariableBase, table=True): # type: ignore - id: Optional[UUID] = Field( + id: UUID | None = Field( default_factory=uuid4, primary_key=True, description="Unique ID for the variable", ) # name is unique per user - created_at: Optional[datetime] = Field( + created_at: datetime | None = Field( default=None, sa_column=Column(DateTime(timezone=True), server_default=func.now(), nullable=True), description="Creation time of the variable", ) - updated_at: Optional[datetime] = Field( + updated_at: datetime | None = Field( default=None, sa_column=Column(DateTime(timezone=True), nullable=True), description="Last update time of the variable", ) - default_fields: Optional[List[str]] = Field(sa_column=Column(JSON)) + default_fields: list[str] | None = Field(sa_column=Column(JSON)) # foreign key to user table user_id: UUID = Field(description="User ID associated with this variable", foreign_key="user.id") user: "User" = Relationship(back_populates="variables") class VariableCreate(VariableBase): - created_at: Optional[datetime] = Field(default_factory=utc_now, description="Creation time of the variable") + created_at: datetime | None = Field(default_factory=utc_now, description="Creation time of the variable") - updated_at: Optional[datetime] = Field(default_factory=utc_now, description="Creation time of the variable") + updated_at: datetime | None = Field(default_factory=utc_now, description="Creation time of the variable") class VariableRead(SQLModel): id: UUID - name: Optional[str] = Field(None, description="Name of the variable") - type: Optional[str] = Field(None, description="Type of the variable") - value: Optional[str] = Field(None, description="Encrypted value of the variable") - default_fields: Optional[List[str]] = Field(None, description="Default fields for the variable") + name: str | None = Field(None, description="Name of the variable") + type: str | None = Field(None, description="Type of the variable") + value: str | None = Field(None, description="Encrypted value of the variable") + default_fields: list[str] | None = Field(None, description="Default fields for the variable") @field_validator("value") @classmethod @@ -68,6 +68,6 @@ class VariableRead(SQLModel): class VariableUpdate(SQLModel): id: UUID # Include the ID for updating - name: Optional[str] = Field(None, description="Name of the variable") - value: Optional[str] = Field(None, description="Encrypted value of the variable") - default_fields: Optional[List[str]] = Field(None, description="Default fields for the variable") + name: str | None = Field(None, description="Name of the variable") + value: str | None = Field(None, description="Encrypted value of the variable") + default_fields: list[str] | None = Field(None, description="Default fields for the variable") diff --git a/src/backend/base/langflow/services/database/models/vertex_builds/crud.py b/src/backend/base/langflow/services/database/models/vertex_builds/crud.py index 972b3d45d..69603adb0 100644 --- a/src/backend/base/langflow/services/database/models/vertex_builds/crud.py +++ b/src/backend/base/langflow/services/database/models/vertex_builds/crud.py @@ -1,4 +1,3 @@ -from typing import Optional from uuid import UUID from sqlalchemy.exc import IntegrityError @@ -7,7 +6,7 @@ from sqlmodel import Session, col, delete, select from langflow.services.database.models.vertex_builds.model import VertexBuildBase, VertexBuildTable -def get_vertex_builds_by_flow_id(db: Session, flow_id: UUID, limit: Optional[int] = 1000) -> list[VertexBuildTable]: +def get_vertex_builds_by_flow_id(db: Session, flow_id: UUID, limit: int | None = 1000) -> list[VertexBuildTable]: stmt = ( select(VertexBuildTable) .where(VertexBuildTable.flow_id == flow_id) diff --git a/src/backend/base/langflow/services/database/models/vertex_builds/model.py b/src/backend/base/langflow/services/database/models/vertex_builds/model.py index 60c85f6ec..ab535dbc2 100644 --- a/src/backend/base/langflow/services/database/models/vertex_builds/model.py +++ b/src/backend/base/langflow/services/database/models/vertex_builds/model.py @@ -1,5 +1,5 @@ from datetime import datetime, timezone -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from uuid import UUID, uuid4 from pydantic import BaseModel, field_serializer, field_validator @@ -12,9 +12,9 @@ if TYPE_CHECKING: class VertexBuildBase(SQLModel): timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) id: str = Field(nullable=False) - data: Optional[dict] = Field(default=None, sa_column=Column(JSON)) - artifacts: Optional[dict] = Field(default=None, sa_column=Column(JSON)) - params: Optional[str] = Field(nullable=True) + data: dict | None = Field(default=None, sa_column=Column(JSON)) + artifacts: dict | None = Field(default=None, sa_column=Column(JSON)) + params: str | None = Field(nullable=True) valid: bool = Field(nullable=False) flow_id: UUID = Field(foreign_key="flow.id") @@ -41,7 +41,7 @@ class VertexBuildBase(SQLModel): class VertexBuildTable(VertexBuildBase, table=True): # type: ignore __tablename__ = "vertex_build" - build_id: Optional[UUID] = Field(default_factory=uuid4, primary_key=True) + build_id: UUID | None = Field(default_factory=uuid4, primary_key=True) flow: "Flow" = Relationship(back_populates="vertex_builds") diff --git a/src/backend/base/langflow/services/database/service.py b/src/backend/base/langflow/services/database/service.py index 7003abd85..b3ac7c637 100644 --- a/src/backend/base/langflow/services/database/service.py +++ b/src/backend/base/langflow/services/database/service.py @@ -2,7 +2,7 @@ import time from contextlib import contextmanager from datetime import datetime from pathlib import Path -from typing import TYPE_CHECKING, Optional, Type +from typing import TYPE_CHECKING import sqlalchemy as sa from alembic import command, util @@ -82,7 +82,7 @@ class DatabaseService(Service): from sqlite3 import Connection as sqliteConnection if isinstance(dbapi_connection, sqliteConnection): - pragmas: Optional[dict] = self.settings_service.settings.sqlite_pragmas + pragmas: dict | None = self.settings_service.settings.sqlite_pragmas pragmas_list = [] for key, val in pragmas.items() or {}: pragmas_list.append(f"PRAGMA {key} = {val}") @@ -126,7 +126,7 @@ class DatabaseService(Service): def check_schema_health(self) -> bool: inspector = inspect(self.engine) - model_mapping: dict[str, Type[SQLModel]] = { + model_mapping: dict[str, type[SQLModel]] = { "flow": models.Flow, "user": models.User, "apikey": models.ApiKey, @@ -204,7 +204,7 @@ class DatabaseService(Service): buffer.write(f"{datetime.now().isoformat()}: Checking migrations\n") command.check(alembic_cfg) except Exception as exc: - if isinstance(exc, (util.exc.CommandError, util.exc.AutogenerateDiffsDetected)): + if isinstance(exc, util.exc.CommandError | util.exc.AutogenerateDiffsDetected): command.upgrade(alembic_cfg, "head") time.sleep(3) diff --git a/src/backend/base/langflow/services/database/utils.py b/src/backend/base/langflow/services/database/utils.py index e35f70f3b..550da7fda 100644 --- a/src/backend/base/langflow/services/database/utils.py +++ b/src/backend/base/langflow/services/database/utils.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import json from contextlib import contextmanager from dataclasses import dataclass @@ -13,8 +15,6 @@ from langflow.services.deps import get_monitor_service if TYPE_CHECKING: from langflow.services.database.service import DatabaseService -from typing import Dict, List - def migrate_messages_from_monitor_service_to_database(session: Session) -> bool: from langflow.schema.message import Message @@ -34,7 +34,7 @@ def migrate_messages_from_monitor_service_to_database(session: Session) -> bool: logger.info("No messages to migrate.") return True - original_messages: List[Dict] = messages_df.to_dict(orient="records") + original_messages: list[dict] = messages_df.to_dict(orient="records") db_messages = session.exec(select(MessageTable)).all() db_messages = [msg[0] for msg in db_messages] # type: ignore @@ -90,7 +90,7 @@ def initialize_database(fix_migration: bool = False): logger.debug("Initializing database") from langflow.services.deps import get_db_service - database_service: "DatabaseService" = get_db_service() + database_service: DatabaseService = get_db_service() try: database_service.create_db_and_tables() except Exception as exc: @@ -130,7 +130,7 @@ def initialize_database(fix_migration: bool = False): @contextmanager -def session_getter(db_service: "DatabaseService"): +def session_getter(db_service: DatabaseService): try: session = Session(db_service.engine) yield session diff --git a/src/backend/base/langflow/services/deps.py b/src/backend/base/langflow/services/deps.py index 827e1ca53..ebea3a35a 100644 --- a/src/backend/base/langflow/services/deps.py +++ b/src/backend/base/langflow/services/deps.py @@ -1,5 +1,6 @@ +from collections.abc import Generator from contextlib import contextmanager -from typing import TYPE_CHECKING, Generator +from typing import TYPE_CHECKING from loguru import logger diff --git a/src/backend/base/langflow/services/factory.py b/src/backend/base/langflow/services/factory.py index 49bea7db2..41182924b 100644 --- a/src/backend/base/langflow/services/factory.py +++ b/src/backend/base/langflow/services/factory.py @@ -1,6 +1,6 @@ import importlib import inspect -from typing import TYPE_CHECKING, Type, get_type_hints +from typing import TYPE_CHECKING, get_type_hints from cachetools import LRUCache, cached from loguru import logger @@ -23,7 +23,7 @@ class ServiceFactory: raise self.service_class(*args, **kwargs) -def hash_factory(factory: Type[ServiceFactory]) -> str: +def hash_factory(factory: type[ServiceFactory]) -> str: return factory.service_class.__name__ @@ -31,14 +31,14 @@ def hash_dict(d: dict) -> str: return str(d) -def hash_infer_service_types_args(factory_class: Type[ServiceFactory], available_services=None) -> str: +def hash_infer_service_types_args(factory_class: type[ServiceFactory], available_services=None) -> str: factory_hash = hash_factory(factory_class) services_hash = hash_dict(available_services) return f"{factory_hash}_{services_hash}" @cached(cache=LRUCache(maxsize=10), key=hash_infer_service_types_args) -def infer_service_types(factory_class: Type[ServiceFactory], available_services=None) -> list["ServiceType"]: +def infer_service_types(factory_class: type[ServiceFactory], available_services=None) -> list["ServiceType"]: create_method = factory_class.create type_hints = get_type_hints(create_method, globalns=available_services) service_types = [] diff --git a/src/backend/base/langflow/services/manager.py b/src/backend/base/langflow/services/manager.py index b534f1331..3fe82ecc5 100644 --- a/src/backend/base/langflow/services/manager.py +++ b/src/backend/base/langflow/services/manager.py @@ -1,7 +1,9 @@ +from __future__ import annotations + import asyncio import importlib import inspect -from typing import TYPE_CHECKING, Dict, Optional +from typing import TYPE_CHECKING from loguru import logger @@ -23,7 +25,7 @@ class ServiceManager: """ def __init__(self): - self.services: Dict[str, "Service"] = {} + self.services: dict[str, Service] = {} self.factories = {} self.register_factories() self.keyed_lock = KeyedMemoryLockManager() @@ -38,7 +40,7 @@ class ServiceManager: def register_factory( self, - service_factory: "ServiceFactory", + service_factory: ServiceFactory, ): """ Registers a new factory with dependencies. @@ -47,7 +49,7 @@ class ServiceManager: service_name = service_factory.service_class.name self.factories[service_name] = service_factory - def get(self, service_name: "ServiceType", default: Optional["ServiceFactory"] = None) -> "Service": + def get(self, service_name: ServiceType, default: ServiceFactory | None = None) -> Service: """ Get (or create) a service by its name. """ @@ -58,7 +60,7 @@ class ServiceManager: return self.services[service_name] - def _create_service(self, service_name: "ServiceType", default: Optional["ServiceFactory"] = None): + def _create_service(self, service_name: ServiceType, default: ServiceFactory | None = None): """ Create a new service given its name, handling dependencies. """ @@ -81,14 +83,14 @@ class ServiceManager: self.services[service_name] = self.factories[service_name].create(**dependent_services) self.services[service_name].set_ready() - def _validate_service_creation(self, service_name: "ServiceType", default: Optional["ServiceFactory"] = None): + def _validate_service_creation(self, service_name: ServiceType, default: ServiceFactory | None = None): """ Validate whether the service can be created. """ if service_name not in self.factories and default is None: raise NoFactoryRegisteredError(f"No factory registered for the service class '{service_name.name}'") - def update(self, service_name: "ServiceType"): + def update(self, service_name: ServiceType): """ Update a service by its name. """ diff --git a/src/backend/base/langflow/services/monitor/service.py b/src/backend/base/langflow/services/monitor/service.py index 2f1b6ef4e..6a00e9e38 100644 --- a/src/backend/base/langflow/services/monitor/service.py +++ b/src/backend/base/langflow/services/monitor/service.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import TYPE_CHECKING, List +from typing import TYPE_CHECKING from platformdirs import user_cache_dir @@ -85,7 +85,7 @@ class MonitorService(Service): return df.to_dict(orient="records") - def delete_transactions(self, ids: List[int]) -> None: + def delete_transactions(self, ids: list[int]) -> None: with new_duckdb_locked_connection(self.db_path, read_only=False) as conn: conn.execute(f"DELETE FROM transactions WHERE index in ({','.join(map(str, ids))})") conn.commit() diff --git a/src/backend/base/langflow/services/monitor/utils.py b/src/backend/base/langflow/services/monitor/utils.py index 60c17dbe9..21d5d24a9 100644 --- a/src/backend/base/langflow/services/monitor/utils.py +++ b/src/backend/base/langflow/services/monitor/utils.py @@ -1,6 +1,6 @@ from contextlib import contextmanager from pathlib import Path -from typing import TYPE_CHECKING, Any, Dict, Type, Union +from typing import TYPE_CHECKING, Any import duckdb from loguru import logger @@ -22,7 +22,7 @@ def get_table_schema_as_dict(conn: duckdb.DuckDBPyConnection, table_name: str) - return schema -def model_to_sql_column_definitions(model: Type[BaseModel]) -> dict: +def model_to_sql_column_definitions(model: type[BaseModel]) -> dict: columns = {} for field_name, field_type in model.model_fields.items(): if hasattr(field_type.annotation, "__args__") and field_type.annotation is not None: @@ -48,7 +48,7 @@ def model_to_sql_column_definitions(model: Type[BaseModel]) -> dict: return columns -def drop_and_create_table_if_schema_mismatch(db_path: str, table_name: str, model: Type[BaseModel]): +def drop_and_create_table_if_schema_mismatch(db_path: str, table_name: str, model: type[BaseModel]): with new_duckdb_locked_connection(db_path) as conn: # Get the current schema from the database try: @@ -79,7 +79,7 @@ def drop_and_create_table_if_schema_mismatch(db_path: str, table_name: str, mode @contextmanager -def new_duckdb_locked_connection(db_path: Union[str, Path], read_only=False): +def new_duckdb_locked_connection(db_path: str | Path, read_only=False): with worker_lock_manager.lock("duckdb"): with duckdb.connect(str(db_path), read_only=read_only) as conn: yield conn @@ -88,8 +88,8 @@ def new_duckdb_locked_connection(db_path: Union[str, Path], read_only=False): def add_row_to_table( conn: duckdb.DuckDBPyConnection, table_name: str, - model: Type, - monitor_data: Union[Dict[str, Any], BaseModel], + model: type[BaseModel], + monitor_data: dict[str, Any] | BaseModel, ): # Validate the data with the Pydantic model if isinstance(monitor_data, model): diff --git a/src/backend/base/langflow/services/plugins/langfuse_plugin.py b/src/backend/base/langflow/services/plugins/langfuse_plugin.py index b65c27733..242e656c3 100644 --- a/src/backend/base/langflow/services/plugins/langfuse_plugin.py +++ b/src/backend/base/langflow/services/plugins/langfuse_plugin.py @@ -65,7 +65,7 @@ class LangfusePlugin(CallbackPlugin): def get(self): return LangfuseInstance.get() - def get_callback(self, _id: Optional[str] = None): + def get_callback(self, _id: str | None = None): if _id is None: _id = "default" diff --git a/src/backend/base/langflow/services/plugins/service.py b/src/backend/base/langflow/services/plugins/service.py index 4b08eebd5..6b8ac0fb1 100644 --- a/src/backend/base/langflow/services/plugins/service.py +++ b/src/backend/base/langflow/services/plugins/service.py @@ -1,7 +1,7 @@ import importlib import inspect import os -from typing import TYPE_CHECKING, Union +from typing import TYPE_CHECKING from loguru import logger @@ -45,7 +45,7 @@ class PluginService(Service): self.plugins[plugin_name] = plugin_instance plugin_instance.initialize() - def get_plugin(self, plugin_name) -> Union[BasePlugin, None]: + def get_plugin(self, plugin_name) -> BasePlugin | None: return self.plugins.get(plugin_name) def get(self, plugin_name): diff --git a/src/backend/base/langflow/services/session/service.py b/src/backend/base/langflow/services/session/service.py index e0feea4e0..30418c151 100644 --- a/src/backend/base/langflow/services/session/service.py +++ b/src/backend/base/langflow/services/session/service.py @@ -1,4 +1,4 @@ -from typing import Coroutine, Optional +from collections.abc import Coroutine from langflow.services.base import Service from langflow.services.cache.base import CacheService @@ -9,9 +9,9 @@ class SessionService(Service): name = "session_service" def __init__(self, cache_service): - self.cache_service: "CacheService" = cache_service + self.cache_service: CacheService = cache_service - async def load_session(self, key, flow_id: str, data_graph: Optional[dict] = None): + async def load_session(self, key, flow_id: str, data_graph: dict | None = None): # Check if the data is cached if key in self.cache_service: result = self.cache_service.get(key) diff --git a/src/backend/base/langflow/services/settings/base.py b/src/backend/base/langflow/services/settings/base.py index 48d96645c..b62790312 100644 --- a/src/backend/base/langflow/services/settings/base.py +++ b/src/backend/base/langflow/services/settings/base.py @@ -3,7 +3,7 @@ import json import os from pathlib import Path from shutil import copy2 -from typing import Any, List, Literal, Optional, Tuple, Type +from typing import Any, Literal import orjson import yaml @@ -59,7 +59,7 @@ class MyCustomSource(EnvSettingsSource): class Settings(BaseSettings): # Define the default LANGFLOW_DIR - config_dir: Optional[str] = None + config_dir: str | None = None # Define if langflow db should be saved in config dir or # in the langflow directory save_db_in_config_dir: bool = False @@ -67,7 +67,7 @@ class Settings(BaseSettings): dev: bool = False """If True, Langflow will run in development mode.""" - database_url: Optional[str] = None + database_url: str | None = None """Database URL for Langflow. If not provided, Langflow will use a SQLite database.""" pool_size: int = 10 """The number of connections to keep open in the connection pool. If not provided, the default is 10.""" @@ -78,7 +78,7 @@ class Settings(BaseSettings): """The number of seconds to wait before giving up on a lock to released or establishing a connection to the database.""" # sqlite configuration - sqlite_pragmas: Optional[dict] = {"synchronous": "NORMAL", "journal_mode": "WAL"} + sqlite_pragmas: dict | None = {"synchronous": "NORMAL", "journal_mode": "WAL"} """SQLite pragmas to use when connecting to the database.""" # cache configuration @@ -95,28 +95,26 @@ class Settings(BaseSettings): """The port on which Langflow will expose Prometheus metrics. 9090 is the default port.""" remove_api_keys: bool = False - components_path: List[str] = [] + components_path: list[str] = [] langchain_cache: str = "InMemoryCache" - load_flows_path: Optional[str] = None + load_flows_path: str | None = None # Redis redis_host: str = "localhost" redis_port: int = 6379 redis_db: int = 0 - redis_url: Optional[str] = None + redis_url: str | None = None redis_cache_expire: int = 3600 # Sentry - sentry_dsn: Optional[str] = None - sentry_traces_sample_rate: Optional[float] = 1.0 - sentry_profiles_sample_rate: Optional[float] = 1.0 + sentry_dsn: str | None = None + sentry_traces_sample_rate: float | None = 1.0 + sentry_profiles_sample_rate: float | None = 1.0 - store: Optional[bool] = True - store_url: Optional[str] = "https://api.langflow.store" - download_webhook_url: Optional[str] = ( - "https://api.langflow.store/flows/trigger/ec611a61-8460-4438-b187-a4f65e5559d4" - ) - like_webhook_url: Optional[str] = "https://api.langflow.store/flows/trigger/64275852-ec00-45c1-984e-3bff814732da" + store: bool | None = True + store_url: str | None = "https://api.langflow.store" + download_webhook_url: str | None = "https://api.langflow.store/flows/trigger/ec611a61-8460-4438-b187-a4f65e5559d4" + like_webhook_url: str | None = "https://api.langflow.store/flows/trigger/64275852-ec00-45c1-984e-3bff814732da" storage_type: str = "local" @@ -339,12 +337,12 @@ class Settings(BaseSettings): @classmethod def settings_customise_sources( cls, - settings_cls: Type[BaseSettings], + settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource, - ) -> Tuple[PydanticBaseSettingsSource, ...]: + ) -> tuple[PydanticBaseSettingsSource, ...]: return (MyCustomSource(settings_cls),) @@ -362,7 +360,7 @@ def load_settings_from_yaml(file_path: str) -> Settings: file_path = os.path.join(current_path, file_path) - with open(file_path, "r") as f: + with open(file_path) as f: settings_dict = yaml.safe_load(f) settings_dict = {k.upper(): v for k, v in settings_dict.items()} diff --git a/src/backend/base/langflow/services/settings/manager.py b/src/backend/base/langflow/services/settings/manager.py index d7d2184f3..fc127a5ea 100644 --- a/src/backend/base/langflow/services/settings/manager.py +++ b/src/backend/base/langflow/services/settings/manager.py @@ -25,7 +25,7 @@ class SettingsService(Service): file_path = os.path.join(current_path, file_path) - with open(file_path, "r") as f: + with open(file_path) as f: settings_dict = yaml.safe_load(f) settings_dict = {k.upper(): v for k, v in settings_dict.items()} diff --git a/src/backend/base/langflow/services/socket/utils.py b/src/backend/base/langflow/services/socket/utils.py index fb9533de9..550145979 100644 --- a/src/backend/base/langflow/services/socket/utils.py +++ b/src/backend/base/langflow/services/socket/utils.py @@ -1,5 +1,5 @@ import time -from typing import Callable +from collections.abc import Callable import socketio # type: ignore from sqlmodel import select diff --git a/src/backend/base/langflow/services/state/service.py b/src/backend/base/langflow/services/state/service.py index b56f95148..8936faea2 100644 --- a/src/backend/base/langflow/services/state/service.py +++ b/src/backend/base/langflow/services/state/service.py @@ -1,6 +1,6 @@ from collections import defaultdict +from collections.abc import Callable from threading import Lock -from typing import Callable from loguru import logger diff --git a/src/backend/base/langflow/services/store/schema.py b/src/backend/base/langflow/services/store/schema.py index 0c37e1166..6eee3cd08 100644 --- a/src/backend/base/langflow/services/store/schema.py +++ b/src/backend/base/langflow/services/store/schema.py @@ -1,4 +1,3 @@ -from typing import List, Optional from uuid import UUID from pydantic import BaseModel, field_validator @@ -6,12 +5,12 @@ from pydantic import BaseModel, field_validator class TagResponse(BaseModel): id: UUID - name: Optional[str] + name: str | None class UsersLikesResponse(BaseModel): - likes_count: Optional[int] - liked_by_user: Optional[bool] + likes_count: int | None + liked_by_user: bool | None class CreateComponentResponse(BaseModel): @@ -19,22 +18,22 @@ class CreateComponentResponse(BaseModel): class TagsIdResponse(BaseModel): - tags_id: Optional[TagResponse] + tags_id: TagResponse | None class ListComponentResponse(BaseModel): - id: Optional[UUID] = None - name: Optional[str] = None - description: Optional[str] = None - liked_by_count: Optional[int] = None - liked_by_user: Optional[bool] = None - is_component: Optional[bool] = None - metadata: Optional[dict] = {} - user_created: Optional[dict] = {} - tags: Optional[List[TagResponse]] = None - downloads_count: Optional[int] = None - last_tested_version: Optional[str] = None - private: Optional[bool] = None + id: UUID | None = None + name: str | None = None + description: str | None = None + liked_by_count: int | None = None + liked_by_user: bool | None = None + is_component: bool | None = None + metadata: dict | None = {} + user_created: dict | None = {} + tags: list[TagResponse] | None = None + downloads_count: int | None = None + last_tested_version: str | None = None + private: bool | None = None # tags comes as a TagsIdResponse but we want to return a list of TagResponse @field_validator("tags", mode="before") @@ -51,26 +50,26 @@ class ListComponentResponse(BaseModel): class ListComponentResponseModel(BaseModel): - count: Optional[int] = 0 + count: int | None = 0 authorized: bool - results: Optional[List[ListComponentResponse]] + results: list[ListComponentResponse] | None class DownloadComponentResponse(BaseModel): id: UUID - name: Optional[str] - description: Optional[str] - data: Optional[dict] - is_component: Optional[bool] - metadata: Optional[dict] = {} + name: str | None + description: str | None + data: dict | None + is_component: bool | None + metadata: dict | None = {} class StoreComponentCreate(BaseModel): name: str - description: Optional[str] + description: str | None data: dict - tags: Optional[List[str]] - parent: Optional[UUID] = None - is_component: Optional[bool] - last_tested_version: Optional[str] = None - private: Optional[bool] = True + tags: list[str] | None + parent: UUID | None = None + is_component: bool | None + last_tested_version: str | None = None + private: bool | None = True diff --git a/src/backend/base/langflow/services/store/service.py b/src/backend/base/langflow/services/store/service.py index e0d3649d0..e827b28a9 100644 --- a/src/backend/base/langflow/services/store/service.py +++ b/src/backend/base/langflow/services/store/service.py @@ -1,5 +1,5 @@ import json -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple +from typing import TYPE_CHECKING, Any from uuid import UUID import httpx @@ -27,11 +27,11 @@ if TYPE_CHECKING: from contextlib import asynccontextmanager from contextvars import ContextVar -user_data_var: ContextVar[Optional[Dict[str, Any]]] = ContextVar("user_data", default=None) +user_data_var: ContextVar[dict[str, Any] | None] = ContextVar("user_data", default=None) @asynccontextmanager -async def user_data_context(store_service: "StoreService", api_key: Optional[str] = None): +async def user_data_context(store_service: "StoreService", api_key: str | None = None): # Fetch and set user data to the context variable if api_key: try: @@ -49,7 +49,7 @@ async def user_data_context(store_service: "StoreService", api_key: Optional[str user_data_var.set(None) -def get_id_from_search_string(search_string: str) -> Optional[str]: +def get_id_from_search_string(search_string: str) -> str | None: """ Extracts the ID from a search string. @@ -59,7 +59,7 @@ def get_id_from_search_string(search_string: str) -> Optional[str]: Returns: Optional[str]: The extracted ID, or None if no ID is found. """ - possible_id: Optional[str] = search_string + possible_id: str | None = search_string if "www.langflow.store/store/" in search_string: possible_id = search_string.split("/")[-1] @@ -121,8 +121,8 @@ class StoreService(Service): raise ValueError(f"Unexpected error: {exc}") async def _get( - self, url: str, api_key: Optional[str] = None, params: Optional[Dict[str, Any]] = None - ) -> Tuple[List[Dict[str, Any]], Dict[str, Any]]: + self, url: str, api_key: str | None = None, params: dict[str, Any] | None = None + ) -> tuple[list[dict[str, Any]], dict[str, Any]]: """Utility method to perform GET requests.""" if api_key: headers = {"Authorization": f"Bearer {api_key}"} @@ -162,17 +162,17 @@ class StoreService(Service): except Exception as exc: logger.debug(f"Webhook failed: {exc}") - def build_tags_filter(self, tags: List[str]): - tags_filter: Dict[str, Any] = {"tags": {"_and": []}} + def build_tags_filter(self, tags: list[str]): + tags_filter: dict[str, Any] = {"tags": {"_and": []}} for tag in tags: tags_filter["tags"]["_and"].append({"_some": {"tags_id": {"name": {"_eq": tag}}}}) return tags_filter async def count_components( self, - filter_conditions: List[Dict[str, Any]], - api_key: Optional[str] = None, - use_api_key: Optional[bool] = False, + filter_conditions: list[dict[str, Any]], + api_key: str | None = None, + use_api_key: bool | None = False, ) -> int: params = {"aggregate": json.dumps({"count": "*"})} if filter_conditions: @@ -187,7 +187,7 @@ class StoreService(Service): def build_search_filter_conditions(query: str): # instead of build the param ?search=query, we will build the filter # that will use _icontains (case insensitive) - conditions: Dict[str, Any] = {"_or": []} + conditions: dict[str, Any] = {"_or": []} conditions["_or"].append({"name": {"_icontains": query}}) conditions["_or"].append({"description": {"_icontains": query}}) conditions["_or"].append({"tags": {"tags_id": {"name": {"_icontains": query}}}}) @@ -196,14 +196,14 @@ class StoreService(Service): def build_filter_conditions( self, - component_id: Optional[str] = None, - search: Optional[str] = None, - private: Optional[bool] = None, - tags: Optional[List[str]] = None, - is_component: Optional[bool] = None, - filter_by_user: Optional[bool] = False, - liked: Optional[bool] = False, - store_api_key: Optional[str] = None, + component_id: str | None = None, + search: str | None = None, + private: bool | None = None, + tags: list[str] | None = None, + is_component: bool | None = None, + filter_by_user: bool | None = False, + liked: bool | None = False, + store_api_key: str | None = None, ): filter_conditions = [] @@ -251,15 +251,15 @@ class StoreService(Service): async def query_components( self, - api_key: Optional[str] = None, - sort: Optional[List[str]] = None, + api_key: str | None = None, + sort: list[str] | None = None, page: int = 1, limit: int = 15, - fields: Optional[List[str]] = None, - filter_conditions: Optional[List[Dict[str, Any]]] = None, - use_api_key: Optional[bool] = False, - ) -> Tuple[List[ListComponentResponse], Dict[str, Any]]: - params: Dict[str, Any] = { + fields: list[str] | None = None, + filter_conditions: list[dict[str, Any]] | None = None, + use_api_key: bool | None = False, + ) -> tuple[list[ListComponentResponse], dict[str, Any]]: + params: dict[str, Any] = { "page": page, "limit": limit, "fields": ",".join(fields) if fields is not None else ",".join(self.default_fields), @@ -288,7 +288,7 @@ class StoreService(Service): return results_objects, metadata - async def get_liked_by_user_components(self, component_ids: List[str], api_key: str) -> List[str]: + async def get_liked_by_user_components(self, component_ids: list[str], api_key: str) -> list[str]: # Get fields id # filter should be "id is in component_ids AND liked_by directus_users_id token is api_key" # return the ids @@ -310,7 +310,7 @@ class StoreService(Service): return [result["id"] for result in results] # Which of the components is parent of the user's components - async def get_components_in_users_collection(self, component_ids: List[str], api_key: str): + async def get_components_in_users_collection(self, component_ids: list[str], api_key: str): user_data = user_data_var.get() if not user_data: raise ValueError("No user data") @@ -418,13 +418,13 @@ class StoreService(Service): pass raise ValueError(f"Upload failed: {exc}") - async def get_tags(self) -> List[Dict[str, Any]]: + async def get_tags(self) -> list[dict[str, Any]]: url = f"{self.base_url}/items/tags" params = {"fields": ",".join(["id", "name"])} tags, _ = await self._get(url, api_key=None, params=params) return tags - async def get_user_likes(self, api_key: str) -> List[Dict[str, Any]]: + async def get_user_likes(self, api_key: str) -> list[dict[str, Any]]: url = f"{self.base_url}/users/me" params = { "fields": ",".join(["id", "likes"]), @@ -432,7 +432,7 @@ class StoreService(Service): likes, _ = await self._get(url, api_key, params) return likes - async def get_component_likes_count(self, component_id: str, api_key: Optional[str] = None) -> int: + async def get_component_likes_count(self, component_id: str, api_key: str | None = None) -> int: url = f"{self.components_url}/{component_id}" params = { @@ -485,21 +485,21 @@ class StoreService(Service): async def get_list_component_response_model( self, - component_id: Optional[str] = None, - search: Optional[str] = None, - private: Optional[bool] = None, - tags: Optional[List[str]] = None, - is_component: Optional[bool] = None, - fields: Optional[List[str]] = None, + component_id: str | None = None, + search: str | None = None, + private: bool | None = None, + tags: list[str] | None = None, + is_component: bool | None = None, + fields: list[str] | None = None, filter_by_user: bool = False, liked: bool = False, - store_api_key: Optional[str] = None, - sort: Optional[List[str]] = None, + store_api_key: str | None = None, + sort: list[str] | None = None, page: int = 1, limit: int = 15, ): async with user_data_context(api_key=store_api_key, store_service=self): - filter_conditions: List[Dict[str, Any]] = self.build_filter_conditions( + filter_conditions: list[dict[str, Any]] = self.build_filter_conditions( component_id=component_id, search=search, private=private, @@ -510,9 +510,9 @@ class StoreService(Service): store_api_key=store_api_key, ) - result: List[ListComponentResponse] = [] + result: list[ListComponentResponse] = [] authorized = False - metadata: Dict = {} + metadata: dict = {} comp_count = 0 try: result, metadata = await self.query_components( diff --git a/src/backend/base/langflow/services/store/utils.py b/src/backend/base/langflow/services/store/utils.py index 9d8beb632..3e41ea18a 100644 --- a/src/backend/base/langflow/services/store/utils.py +++ b/src/backend/base/langflow/services/store/utils.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, List +from typing import TYPE_CHECKING import httpx @@ -15,7 +15,7 @@ def process_tags_for_post(component_dict): async def update_components_with_user_data( - components: List["ListComponentResponse"], + components: list["ListComponentResponse"], store_service: "StoreService", store_api_key: str, liked: bool, diff --git a/src/backend/base/langflow/services/task/backends/anyio.py b/src/backend/base/langflow/services/task/backends/anyio.py index 04309b8df..39bbb0490 100644 --- a/src/backend/base/langflow/services/task/backends/anyio.py +++ b/src/backend/base/langflow/services/task/backends/anyio.py @@ -1,5 +1,6 @@ import traceback -from typing import Any, Callable, Optional, Tuple +from collections.abc import Callable +from typing import Any import anyio from loguru import logger @@ -51,7 +52,7 @@ class AnyIOBackend(TaskBackend): async def launch_task( self, task_func: Callable[..., Any], *args: Any, **kwargs: Any - ) -> Tuple[Optional[str], Optional[AnyIOTaskResult]]: + ) -> tuple[str | None, AnyIOTaskResult | None]: """ Launch a new task in an asynchronous manner. diff --git a/src/backend/base/langflow/services/task/backends/base.py b/src/backend/base/langflow/services/task/backends/base.py index 93fbfd858..524d92ada 100644 --- a/src/backend/base/langflow/services/task/backends/base.py +++ b/src/backend/base/langflow/services/task/backends/base.py @@ -1,5 +1,6 @@ from abc import ABC, abstractmethod -from typing import Any, Callable +from collections.abc import Callable +from typing import Any class TaskBackend(ABC): diff --git a/src/backend/base/langflow/services/task/backends/celery.py b/src/backend/base/langflow/services/task/backends/celery.py index db1bb475b..45a3ed7b0 100644 --- a/src/backend/base/langflow/services/task/backends/celery.py +++ b/src/backend/base/langflow/services/task/backends/celery.py @@ -1,4 +1,5 @@ -from typing import Any, Callable +from collections.abc import Callable +from typing import Any from celery.result import AsyncResult # type: ignore diff --git a/src/backend/base/langflow/services/task/service.py b/src/backend/base/langflow/services/task/service.py index cca1645b8..321ac9c74 100644 --- a/src/backend/base/langflow/services/task/service.py +++ b/src/backend/base/langflow/services/task/service.py @@ -1,4 +1,5 @@ -from typing import TYPE_CHECKING, Any, Callable, Coroutine +from collections.abc import Callable, Coroutine +from typing import TYPE_CHECKING, Any from loguru import logger diff --git a/src/backend/base/langflow/services/telemetry/opentelemetry.py b/src/backend/base/langflow/services/telemetry/opentelemetry.py index 81d0d873c..715fe09e9 100644 --- a/src/backend/base/langflow/services/telemetry/opentelemetry.py +++ b/src/backend/base/langflow/services/telemetry/opentelemetry.py @@ -1,7 +1,8 @@ import threading import warnings +from collections.abc import Mapping from enum import Enum -from typing import Any, Dict, Mapping, Tuple, Union +from typing import Any from weakref import WeakValueDictionary from opentelemetry import metrics @@ -43,7 +44,7 @@ class ObservableGaugeWrapper: """ def __init__(self, name: str, description: str, unit: str): - self._values: Dict[Tuple[Tuple[str, str], ...], float] = {} + self._values: dict[tuple[tuple[str, str], ...], float] = {} self._meter = metrics.get_meter(langflow_meter_name) self._gauge = self._meter.create_observable_gauge( name=name, description=description, unit=unit, callbacks=[self._callback] @@ -64,7 +65,7 @@ class Metric: name: str, description: str, type: MetricType, - labels: Dict[str, bool], + labels: dict[str, bool], unit: str = "", ): self.name = name @@ -102,15 +103,15 @@ class ThreadSafeSingletonMetaUsingWeakref(type): if cls not in cls._instances: with cls._lock: if cls not in cls._instances: - instance = super(ThreadSafeSingletonMetaUsingWeakref, cls).__call__(*args, **kwargs) + instance = super().__call__(*args, **kwargs) cls._instances[cls] = instance return cls._instances[cls] class OpenTelemetry(metaclass=ThreadSafeSingletonMetaUsingWeakref): - _metrics_registry: Dict[str, Metric] = dict() + _metrics_registry: dict[str, Metric] = dict() - def _add_metric(self, name: str, description: str, unit: str, metric_type: MetricType, labels: Dict[str, bool]): + def _add_metric(self, name: str, description: str, unit: str, metric_type: MetricType, labels: dict[str, bool]): metric = Metric(name=name, description=description, type=metric_type, unit=unit, labels=labels) self._metrics_registry[name] = metric if labels is None or len(labels) == 0: @@ -136,7 +137,7 @@ class OpenTelemetry(metaclass=ThreadSafeSingletonMetaUsingWeakref): labels={"flow_id": mandatory_label}, ) - _metrics: Dict[str, Union[Counter, ObservableGaugeWrapper, Histogram, UpDownCounter]] = {} + _metrics: dict[str, Counter | ObservableGaugeWrapper | Histogram | UpDownCounter] = {} def __init__(self, prometheus_enabled: bool = True): self._register_metric() diff --git a/src/backend/base/langflow/services/tracing/base.py b/src/backend/base/langflow/services/tracing/base.py index 9b101f71b..f1012f6ed 100644 --- a/src/backend/base/langflow/services/tracing/base.py +++ b/src/backend/base/langflow/services/tracing/base.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from typing import TYPE_CHECKING, Any, Dict, Optional +from typing import TYPE_CHECKING, Any, Optional from uuid import UUID from langflow.services.tracing.schema import Log @@ -25,8 +25,8 @@ class BaseTracer(ABC): trace_id: str, trace_name: str, trace_type: str, - inputs: Dict[str, Any], - metadata: Dict[str, Any] | None = None, + inputs: dict[str, Any], + metadata: dict[str, Any] | None = None, vertex: Optional["Vertex"] = None, ): raise NotImplementedError @@ -36,7 +36,7 @@ class BaseTracer(ABC): self, trace_id: str, trace_name: str, - outputs: Dict[str, Any] | None = None, + outputs: dict[str, Any] | None = None, error: Exception | None = None, logs: list[Log | dict] = [], ): @@ -46,7 +46,7 @@ class BaseTracer(ABC): def end( self, inputs: dict[str, Any], - outputs: Dict[str, Any], + outputs: dict[str, Any], error: Exception | None = None, metadata: dict[str, Any] | None = None, ): diff --git a/src/backend/base/langflow/services/tracing/langfuse.py b/src/backend/base/langflow/services/tracing/langfuse.py index 7ba3657d2..6f8e032f3 100644 --- a/src/backend/base/langflow/services/tracing/langfuse.py +++ b/src/backend/base/langflow/services/tracing/langfuse.py @@ -1,6 +1,6 @@ import os from datetime import datetime -from typing import TYPE_CHECKING, Any, Dict, Optional +from typing import TYPE_CHECKING, Any, Optional from uuid import UUID from loguru import logger @@ -65,8 +65,8 @@ class LangFuseTracer(BaseTracer): trace_id: str, trace_name: str, trace_type: str, - inputs: Dict[str, Any], - metadata: Dict[str, Any] | None = None, + inputs: dict[str, Any], + metadata: dict[str, Any] | None = None, vertex: Optional["Vertex"] = None, ): start_time = datetime.utcnow() @@ -97,7 +97,7 @@ class LangFuseTracer(BaseTracer): self, trace_id: str, trace_name: str, - outputs: Dict[str, Any] | None = None, + outputs: dict[str, Any] | None = None, error: Exception | None = None, logs: list[Log | dict] = [], ): @@ -117,7 +117,7 @@ class LangFuseTracer(BaseTracer): def end( self, inputs: dict[str, Any], - outputs: Dict[str, Any], + outputs: dict[str, Any], error: Exception | None = None, metadata: dict[str, Any] | None = None, ): diff --git a/src/backend/base/langflow/services/tracing/langsmith.py b/src/backend/base/langflow/services/tracing/langsmith.py index 82db6c76c..063afd451 100644 --- a/src/backend/base/langflow/services/tracing/langsmith.py +++ b/src/backend/base/langflow/services/tracing/langsmith.py @@ -2,7 +2,7 @@ import os import traceback import types from datetime import datetime, timezone -from typing import TYPE_CHECKING, Any, Dict, Optional +from typing import TYPE_CHECKING, Any, Optional from uuid import UUID from loguru import logger @@ -63,8 +63,8 @@ class LangSmithTracer(BaseTracer): trace_id: str, trace_name: str, trace_type: str, - inputs: Dict[str, Any], - metadata: Dict[str, Any] | None = None, + inputs: dict[str, Any], + metadata: dict[str, Any] | None = None, vertex: Optional["Vertex"] = None, ): if not self._ready: @@ -82,7 +82,7 @@ class LangSmithTracer(BaseTracer): self._children[trace_name] = child self._child_link: dict[str, str] = {} - def _convert_to_langchain_types(self, io_dict: Dict[str, Any]): + def _convert_to_langchain_types(self, io_dict: dict[str, Any]): converted = {} for key, value in io_dict.items(): converted[key] = self._convert_to_langchain_type(value) @@ -115,7 +115,7 @@ class LangSmithTracer(BaseTracer): self, trace_id: str, trace_name: str, - outputs: Dict[str, Any] | None = None, + outputs: dict[str, Any] | None = None, error: Exception | None = None, logs: list[Log | dict] = [], ): @@ -138,7 +138,7 @@ class LangSmithTracer(BaseTracer): child.post() self._child_link[trace_name] = child.get_url() - def _error_to_string(self, error: Optional[Exception]): + def _error_to_string(self, error: Exception | None): error_message = None if error: string_stacktrace = traceback.format_exception(error) @@ -148,7 +148,7 @@ class LangSmithTracer(BaseTracer): def end( self, inputs: dict[str, Any], - outputs: Dict[str, Any], + outputs: dict[str, Any], error: Exception | None = None, metadata: dict[str, Any] | None = None, ): diff --git a/src/backend/base/langflow/services/tracing/langwatch.py b/src/backend/base/langflow/services/tracing/langwatch.py index aa6469c1f..4c057f6ee 100644 --- a/src/backend/base/langflow/services/tracing/langwatch.py +++ b/src/backend/base/langflow/services/tracing/langwatch.py @@ -1,4 +1,6 @@ -from typing import TYPE_CHECKING, Any, Dict, Optional, cast +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, cast from uuid import UUID import nanoid # type: ignore @@ -33,7 +35,7 @@ class LangWatchTracer(BaseTracer): self.trace = self._client.trace( trace_id=str(self.trace_id), ) - self.spans: dict[str, "ContextSpan"] = {} + self.spans: dict[str, ContextSpan] = {} name_without_id = " - ".join(trace_name.split(" - ")[0:-1]) self.trace.root_span.update( @@ -64,9 +66,9 @@ class LangWatchTracer(BaseTracer): trace_id: str, trace_name: str, trace_type: str, - inputs: Dict[str, Any], - metadata: Dict[str, Any] | None = None, - vertex: Optional["Vertex"] = None, + inputs: dict[str, Any], + metadata: dict[str, Any] | None = None, + vertex: Vertex | None = None, ): if not self._ready: return @@ -97,7 +99,7 @@ class LangWatchTracer(BaseTracer): self, trace_id: str, trace_name: str, - outputs: Dict[str, Any] | None = None, + outputs: dict[str, Any] | None = None, error: Exception | None = None, logs: list[Log | dict] = [], ): @@ -109,7 +111,7 @@ class LangWatchTracer(BaseTracer): def end( self, inputs: dict[str, Any], - outputs: Dict[str, Any], + outputs: dict[str, Any], error: Exception | None = None, metadata: dict[str, Any] | None = None, ): @@ -127,7 +129,7 @@ class LangWatchTracer(BaseTracer): if self.trace.api_key or self._client.api_key: self.trace.deferred_send_spans() - def _convert_to_langwatch_types(self, io_dict: Optional[Dict[str, Any]]): + def _convert_to_langwatch_types(self, io_dict: dict[str, Any] | None): from langwatch.utils import autoconvert_typed_values if io_dict is None: @@ -163,7 +165,7 @@ class LangWatchTracer(BaseTracer): value = cast(dict, value.to_lc_document()) return value - def get_langchain_callback(self) -> Optional["BaseCallbackHandler"]: + def get_langchain_callback(self) -> BaseCallbackHandler | None: if self.trace is None: return None diff --git a/src/backend/base/langflow/services/tracing/service.py b/src/backend/base/langflow/services/tracing/service.py index 3e8a6ad13..6e78591f9 100644 --- a/src/backend/base/langflow/services/tracing/service.py +++ b/src/backend/base/langflow/services/tracing/service.py @@ -2,7 +2,7 @@ import asyncio import os from collections import defaultdict from contextlib import asynccontextmanager -from typing import TYPE_CHECKING, Any, Dict, List, Optional +from typing import TYPE_CHECKING, Any, Optional from uuid import UUID from loguru import logger @@ -155,8 +155,8 @@ class TracingService(Service): trace_id: str, trace_name: str, trace_type: str, - inputs: Dict[str, Any], - metadata: Optional[Dict[str, Any]] = None, + inputs: dict[str, Any], + metadata: dict[str, Any] | None = None, vertex: Optional["Vertex"] = None, ): inputs = self._cleanup_inputs(inputs) @@ -207,8 +207,8 @@ class TracingService(Service): self, component: "Component", trace_name: str, - inputs: Dict[str, Any], - metadata: Optional[Dict[str, Any]] = None, + inputs: dict[str, Any], + metadata: dict[str, Any] | None = None, ): trace_id = trace_name if component._vertex: @@ -237,20 +237,20 @@ class TracingService(Service): def set_outputs( self, trace_name: str, - outputs: Dict[str, Any], - output_metadata: Dict[str, Any] | None = None, + outputs: dict[str, Any], + output_metadata: dict[str, Any] | None = None, ): self.outputs[trace_name] |= outputs or {} self.outputs_metadata[trace_name] |= output_metadata or {} - def _cleanup_inputs(self, inputs: Dict[str, Any]): + def _cleanup_inputs(self, inputs: dict[str, Any]): inputs = inputs.copy() for key in inputs.keys(): if "api_key" in key: inputs[key] = "*****" # avoid logging api_keys for security reasons return inputs - def get_langchain_callbacks(self) -> List["BaseCallbackHandler"]: + def get_langchain_callbacks(self) -> list["BaseCallbackHandler"]: callbacks = [] for tracer in self._tracers.values(): if not tracer.ready: # type: ignore diff --git a/src/backend/base/langflow/services/tracing/utils.py b/src/backend/base/langflow/services/tracing/utils.py index a90c94c99..107d4619f 100644 --- a/src/backend/base/langflow/services/tracing/utils.py +++ b/src/backend/base/langflow/services/tracing/utils.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any from langflow.schema.data import Data @@ -27,7 +27,7 @@ def convert_to_langchain_type(value): return value -def convert_to_langchain_types(io_dict: Dict[str, Any]): +def convert_to_langchain_types(io_dict: dict[str, Any]): converted = {} for key, value in io_dict.items(): converted[key] = convert_to_langchain_type(value) diff --git a/src/backend/base/langflow/services/variable/base.py b/src/backend/base/langflow/services/variable/base.py index 632b03af5..33b1a9680 100644 --- a/src/backend/base/langflow/services/variable/base.py +++ b/src/backend/base/langflow/services/variable/base.py @@ -1,5 +1,4 @@ import abc -from typing import Optional, Union from uuid import UUID from sqlmodel import Session @@ -16,7 +15,7 @@ class VariableService(Service): name = "variable_service" @abc.abstractmethod - def initialize_user_variables(self, user_id: Union[UUID, str], session: Session) -> None: + def initialize_user_variables(self, user_id: UUID | str, session: Session) -> None: """ Initialize user variables. @@ -26,7 +25,7 @@ class VariableService(Service): """ @abc.abstractmethod - def get_variable(self, user_id: Union[UUID, str], name: str, field: str, session: Session) -> str: + def get_variable(self, user_id: UUID | str, name: str, field: str, session: Session) -> str: """ Get a variable value. @@ -41,7 +40,7 @@ class VariableService(Service): """ @abc.abstractmethod - def list_variables(self, user_id: Union[UUID, str], session: Session) -> list[Optional[str]]: + def list_variables(self, user_id: UUID | str, session: Session) -> list[str | None]: """ List all variables. @@ -54,7 +53,7 @@ class VariableService(Service): """ @abc.abstractmethod - def update_variable(self, user_id: Union[UUID, str], name: str, value: str, session: Session) -> Variable: + def update_variable(self, user_id: UUID | str, name: str, value: str, session: Session) -> Variable: """ Update a variable. @@ -69,7 +68,7 @@ class VariableService(Service): """ @abc.abstractmethod - def delete_variable(self, user_id: Union[UUID, str], name: str, session: Session) -> None: + def delete_variable(self, user_id: UUID | str, name: str, session: Session) -> None: """ Delete a variable. @@ -83,7 +82,7 @@ class VariableService(Service): """ @abc.abstractmethod - def delete_variable_by_id(self, user_id: Union[UUID, str], variable_id: UUID, session: Session) -> None: + def delete_variable_by_id(self, user_id: UUID | str, variable_id: UUID, session: Session) -> None: """ Delete a variable by ID. @@ -96,7 +95,7 @@ class VariableService(Service): @abc.abstractmethod def create_variable( self, - user_id: Union[UUID, str], + user_id: UUID | str, name: str, value: str, default_fields: list[str], diff --git a/src/backend/base/langflow/services/variable/kubernetes.py b/src/backend/base/langflow/services/variable/kubernetes.py index 460b84dcc..9d276ffce 100644 --- a/src/backend/base/langflow/services/variable/kubernetes.py +++ b/src/backend/base/langflow/services/variable/kubernetes.py @@ -1,5 +1,4 @@ import os -from typing import Optional, Tuple, Union from uuid import UUID from loguru import logger @@ -20,7 +19,7 @@ class KubernetesSecretService(VariableService, Service): # TODO: settings_service to set kubernetes namespace self.kubernetes_secrets = KubernetesSecretManager() - def initialize_user_variables(self, user_id: Union[UUID, str], session: Session): + def initialize_user_variables(self, user_id: UUID | str, session: Session): # Check for environment variables that should be stored in the database should_or_should_not = "Should" if self.settings_service.settings.store_environment_variables else "Should not" logger.info(f"{should_or_should_not} store environment variables in the kubernetes.") @@ -51,9 +50,9 @@ class KubernetesSecretService(VariableService, Service): def resolve_variable( self, secret_name: str, - user_id: Union[UUID, str], + user_id: UUID | str, name: str, - ) -> Tuple[str, str]: + ) -> tuple[str, str]: variables = self.kubernetes_secrets.get_secret(name=secret_name) if not variables: raise ValueError(f"user_id {user_id} variable not found.") @@ -69,7 +68,7 @@ class KubernetesSecretService(VariableService, Service): def get_variable( self, - user_id: Union[UUID, str], + user_id: UUID | str, name: str, field: str, _session: Session, @@ -85,9 +84,9 @@ class KubernetesSecretService(VariableService, Service): def list_variables( self, - user_id: Union[UUID, str], + user_id: UUID | str, _session: Session, - ) -> list[Optional[str]]: + ) -> list[str | None]: variables = self.kubernetes_secrets.get_secret(name=encode_user_id(user_id)) if not variables: return [] @@ -102,7 +101,7 @@ class KubernetesSecretService(VariableService, Service): def update_variable( self, - user_id: Union[UUID, str], + user_id: UUID | str, name: str, value: str, _session: Session, @@ -111,19 +110,19 @@ class KubernetesSecretService(VariableService, Service): secret_key, _ = self.resolve_variable(secret_name, user_id, name) return self.kubernetes_secrets.update_secret(name=secret_name, data={secret_key: value}) - def delete_variable(self, user_id: Union[UUID, str], name: str, _session: Session) -> None: + def delete_variable(self, user_id: UUID | str, name: str, _session: Session) -> None: secret_name = encode_user_id(user_id) secret_key, _ = self.resolve_variable(secret_name, user_id, name) self.kubernetes_secrets.delete_secret_key(name=secret_name, key=secret_key) return - def delete_variable_by_id(self, user_id: Union[UUID, str], variable_id: UUID | str, _session: Session) -> None: + def delete_variable_by_id(self, user_id: UUID | str, variable_id: UUID | str, _session: Session) -> None: self.delete_variable(user_id, _session, str(variable_id)) def create_variable( self, - user_id: Union[UUID, str], + user_id: UUID | str, name: str, value: str, default_fields: list[str], diff --git a/src/backend/base/langflow/services/variable/kubernetes_secrets.py b/src/backend/base/langflow/services/variable/kubernetes_secrets.py index fc1f17667..09a4fd40a 100644 --- a/src/backend/base/langflow/services/variable/kubernetes_secrets.py +++ b/src/backend/base/langflow/services/variable/kubernetes_secrets.py @@ -1,5 +1,4 @@ from base64 import b64decode, b64encode -from typing import Union from uuid import UUID from kubernetes import client, config # type: ignore @@ -160,7 +159,7 @@ class KubernetesSecretManager: # utility function to encode user_id to base64 lower case and numbers only # this is required by kubernetes secret name restrictions -def encode_user_id(user_id: Union[UUID | str]) -> str: +def encode_user_id(user_id: UUID | str) -> str: # Handle UUID if isinstance(user_id, UUID): return f"uuid-{str(user_id).lower()}"[:253] diff --git a/src/backend/base/langflow/services/variable/service.py b/src/backend/base/langflow/services/variable/service.py index b8cac4136..b816a883b 100644 --- a/src/backend/base/langflow/services/variable/service.py +++ b/src/backend/base/langflow/services/variable/service.py @@ -1,6 +1,6 @@ import os from datetime import datetime, timezone -from typing import TYPE_CHECKING, Optional, Union +from typing import TYPE_CHECKING from uuid import UUID from fastapi import Depends @@ -22,7 +22,7 @@ class DatabaseVariableService(VariableService, Service): def __init__(self, settings_service: "SettingsService"): self.settings_service = settings_service - def initialize_user_variables(self, user_id: Union[UUID, str], session: Session = Depends(get_session)): + def initialize_user_variables(self, user_id: UUID | str, session: Session = Depends(get_session)): # Check for environment variables that should be stored in the database should_or_should_not = "Should" if self.settings_service.settings.store_environment_variables else "Should not" logger.info(f"{should_or_should_not} store environment variables in the database.") @@ -66,7 +66,7 @@ class DatabaseVariableService(VariableService, Service): def get_variable( self, - user_id: Union[UUID, str], + user_id: UUID | str, name: str, field: str, session: Session = Depends(get_session), @@ -88,16 +88,16 @@ class DatabaseVariableService(VariableService, Service): decrypted = auth_utils.decrypt_api_key(variable.value, settings_service=self.settings_service) return decrypted - def get_all(self, user_id: Union[UUID, str], session: Session = Depends(get_session)) -> list[Optional[Variable]]: + def get_all(self, user_id: UUID | str, session: Session = Depends(get_session)) -> list[Variable | None]: return list(session.exec(select(Variable).where(Variable.user_id == user_id)).all()) - def list_variables(self, user_id: Union[UUID, str], session: Session = Depends(get_session)) -> list[Optional[str]]: + def list_variables(self, user_id: UUID | str, session: Session = Depends(get_session)) -> list[str | None]: variables = self.get_all(user_id=user_id, session=session) return [variable.name for variable in variables if variable] def update_variable( self, - user_id: Union[UUID, str], + user_id: UUID | str, name: str, value: str, session: Session = Depends(get_session), @@ -114,8 +114,8 @@ class DatabaseVariableService(VariableService, Service): def update_variable_fields( self, - user_id: Union[UUID, str], - variable_id: Union[UUID, str], + user_id: UUID | str, + variable_id: UUID | str, variable: VariableUpdate, session: Session = Depends(get_session), ): @@ -136,7 +136,7 @@ class DatabaseVariableService(VariableService, Service): def delete_variable( self, - user_id: Union[UUID, str], + user_id: UUID | str, name: str, session: Session = Depends(get_session), ): @@ -147,7 +147,7 @@ class DatabaseVariableService(VariableService, Service): session.delete(variable) session.commit() - def delete_variable_by_id(self, user_id: Union[UUID, str], variable_id: UUID, session: Session): + def delete_variable_by_id(self, user_id: UUID | str, variable_id: UUID, session: Session): variable = session.exec(select(Variable).where(Variable.user_id == user_id, Variable.id == variable_id)).first() if not variable: raise ValueError(f"{variable_id} variable not found.") @@ -156,7 +156,7 @@ class DatabaseVariableService(VariableService, Service): def create_variable( self, - user_id: Union[UUID, str], + user_id: UUID | str, name: str, value: str, default_fields: list[str] = [], diff --git a/src/backend/base/langflow/template/field/base.py b/src/backend/base/langflow/template/field/base.py index e12d49e49..c767151f9 100644 --- a/src/backend/base/langflow/template/field/base.py +++ b/src/backend/base/langflow/template/field/base.py @@ -155,7 +155,7 @@ class Input(BaseModel): # If the user passes CustomComponent as a type insteado of "CustomComponent" we need to convert it to a string # this should be done for all types # How to check if v is a type? - if isinstance(v, (type, _GenericAlias, GenericAlias, _UnionGenericAlias)): + if isinstance(v, type | _GenericAlias | GenericAlias | _UnionGenericAlias): v = post_process_type(v)[0] v = format_type(v) elif not isinstance(v, str): diff --git a/src/backend/base/langflow/type_extraction/type_extraction.py b/src/backend/base/langflow/type_extraction/type_extraction.py index 6474977a3..309b653b2 100644 --- a/src/backend/base/langflow/type_extraction/type_extraction.py +++ b/src/backend/base/langflow/type_extraction/type_extraction.py @@ -2,7 +2,7 @@ import re from collections.abc import Sequence as SequenceABC from itertools import chain from types import GenericAlias -from typing import Any, List, Union +from typing import Any, Union def extract_inner_type_from_generic_alias(return_type: GenericAlias) -> Any: @@ -59,7 +59,7 @@ def post_process_type(_type): Union[List[Any], Any]: The processed return type. """ - if hasattr(_type, "__origin__") and _type.__origin__ in [list, List, SequenceABC]: + if hasattr(_type, "__origin__") and _type.__origin__ in [list, list, SequenceABC]: _type = extract_inner_type_from_generic_alias(_type) # If the return type is not a Union, then we just return it as a list diff --git a/src/backend/base/langflow/utils/constants.py b/src/backend/base/langflow/utils/constants.py index 0e3777fd8..5bccdbfef 100644 --- a/src/backend/base/langflow/utils/constants.py +++ b/src/backend/base/langflow/utils/constants.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List +from typing import Any OPENAI_MODELS = [ "text-davinci-003", @@ -65,7 +65,7 @@ DIRECT_TYPES = [ ] -LOADERS_INFO: List[Dict[str, Any]] = [ +LOADERS_INFO: list[dict[str, Any]] = [ { "loader": "AirbyteJSONLoader", "name": "Airbyte JSON (.jsonl)", diff --git a/src/backend/base/langflow/utils/payload.py b/src/backend/base/langflow/utils/payload.py index 0e2f0fc7a..8574e4db9 100644 --- a/src/backend/base/langflow/utils/payload.py +++ b/src/backend/base/langflow/utils/payload.py @@ -1,6 +1,5 @@ import contextlib import re -from typing import Dict def extract_input_variables(nodes): @@ -40,7 +39,7 @@ def get_root_vertex(graph): return next((node for node in graph.vertices if node.id not in incoming_edges), None) -def build_json(root, graph) -> Dict: +def build_json(root, graph) -> dict: if "node" not in root.data: # If the root node has no "node" key, then it has only one child, # which is the target of the single outgoing edge diff --git a/src/backend/base/langflow/utils/schemas.py b/src/backend/base/langflow/utils/schemas.py index 2689aeb56..833455502 100644 --- a/src/backend/base/langflow/utils/schemas.py +++ b/src/backend/base/langflow/utils/schemas.py @@ -1,5 +1,4 @@ import enum -from typing import Dict, List, Optional, Union from langchain_core.messages import BaseMessage from pydantic import BaseModel, field_validator, model_validator @@ -20,13 +19,13 @@ class File(TypedDict): class ChatOutputResponse(BaseModel): """Chat output response schema.""" - message: Union[str, List[Union[str, Dict]]] - sender: Optional[str] = MESSAGE_SENDER_AI - sender_name: Optional[str] = MESSAGE_SENDER_NAME_AI - session_id: Optional[str] = None - stream_url: Optional[str] = None - component_id: Optional[str] = None - files: List[File] = [] + message: str | list[str | dict] + sender: str | None = MESSAGE_SENDER_AI + sender_name: str | None = MESSAGE_SENDER_NAME_AI + session_id: str | None = None + stream_url: str | None = None + component_id: str | None = None + files: list[File] = [] type: str @field_validator("files", mode="before") @@ -72,8 +71,8 @@ class ChatOutputResponse(BaseModel): def from_message( cls, message: BaseMessage, - sender: Optional[str] = MESSAGE_SENDER_AI, - sender_name: Optional[str] = MESSAGE_SENDER_NAME_AI, + sender: str | None = MESSAGE_SENDER_AI, + sender_name: str | None = MESSAGE_SENDER_NAME_AI, ): """Build chat output response from message.""" content = message.content @@ -101,7 +100,7 @@ class ChatOutputResponse(BaseModel): class DataOutputResponse(BaseModel): """Data output response schema.""" - data: List[Optional[Dict]] + data: list[dict | None] class ContainsEnumMeta(enum.EnumMeta): diff --git a/src/backend/base/langflow/utils/util.py b/src/backend/base/langflow/utils/util.py index b385d1631..a4cf3f6f4 100644 --- a/src/backend/base/langflow/utils/util.py +++ b/src/backend/base/langflow/utils/util.py @@ -5,7 +5,7 @@ import json import re from functools import wraps from pathlib import Path -from typing import Any, Dict, List, Optional, Union +from typing import Any from docstring_parser import parse @@ -25,7 +25,7 @@ def remove_ansi_escape_codes(text): return re.sub(r"\x1b\[[0-9;]*[a-zA-Z]", "", text) -def build_template_from_function(name: str, type_to_loader_dict: Dict, add_function: bool = False): +def build_template_from_function(name: str, type_to_loader_dict: dict, add_function: bool = False): classes = [item.__annotations__["return"].__name__ for item in type_to_loader_dict.values()] # Raise error if name is not in chains @@ -74,7 +74,7 @@ def build_template_from_function(name: str, type_to_loader_dict: Dict, add_funct def build_template_from_method( class_name: str, method_name: str, - type_to_cls_dict: Dict, + type_to_cls_dict: dict, add_function: bool = False, ): classes = [item.__name__ for item in type_to_cls_dict.values()] @@ -196,7 +196,7 @@ def sync_to_async(func): return async_wrapper -def format_dict(dictionary: Dict[str, Any], class_name: Optional[str] = None) -> Dict[str, Any]: +def format_dict(dictionary: dict[str, Any], class_name: str | None = None) -> dict[str, Any]: """ Formats a dictionary by removing certain keys and modifying the values of other keys. @@ -209,7 +209,7 @@ def format_dict(dictionary: Dict[str, Any], class_name: Optional[str] = None) -> if key in ["_type"]: continue - _type: Union[str, type] = get_type(value) + _type: str | type = get_type(value) if "BaseModel" in str(_type): continue @@ -246,7 +246,7 @@ def get_type_from_union_literal(union_literal: str) -> str: return union_literal -def get_type(value: Any) -> Union[str, type]: +def get_type(value: Any) -> str | type: """ Retrieves the type value from the dictionary. @@ -259,7 +259,7 @@ def get_type(value: Any) -> Union[str, type]: return _type if isinstance(_type, str) else _type.__name__ -def remove_optional_wrapper(_type: Union[str, type]) -> str: +def remove_optional_wrapper(_type: str | type) -> str: """ Removes the 'Optional' wrapper from the type string. @@ -274,7 +274,7 @@ def remove_optional_wrapper(_type: Union[str, type]) -> str: return _type -def check_list_type(_type: str, value: Dict[str, Any]) -> str: +def check_list_type(_type: str, value: dict[str, Any]) -> str: """ Checks if the type is a list type and modifies the value accordingly. @@ -319,7 +319,7 @@ def get_formatted_type(key: str, _type: str) -> str: return _type -def should_show_field(value: Dict[str, Any], key: str) -> bool: +def should_show_field(value: dict[str, Any], key: str) -> bool: """ Determines if the field should be shown or not. @@ -361,7 +361,7 @@ def is_multiline_field(key: str) -> bool: } -def set_dict_file_attributes(value: Dict[str, Any]) -> None: +def set_dict_file_attributes(value: dict[str, Any]) -> None: """ Sets the file attributes for the 'dict_' key. """ @@ -369,7 +369,7 @@ def set_dict_file_attributes(value: Dict[str, Any]) -> None: value["fileTypes"] = [".json", ".yaml", ".yml"] -def replace_default_value_with_actual(value: Dict[str, Any]) -> None: +def replace_default_value_with_actual(value: dict[str, Any]) -> None: """ Replaces the default value with the actual value. """ @@ -378,14 +378,14 @@ def replace_default_value_with_actual(value: Dict[str, Any]) -> None: value.pop("default") -def set_headers_value(value: Dict[str, Any]) -> None: +def set_headers_value(value: dict[str, Any]) -> None: """ Sets the value for the 'headers' key. """ value["value"] = """{"Authorization": "Bearer "}""" -def add_options_to_field(value: Dict[str, Any], class_name: Optional[str], key: str) -> None: +def add_options_to_field(value: dict[str, Any], class_name: str | None, key: str) -> None: """ Adds options to the field based on the class name and key. """ @@ -402,7 +402,7 @@ def add_options_to_field(value: Dict[str, Any], class_name: Optional[str], key: value["value"] = options_map[class_name][0] -def build_loader_repr_from_data(data: List[Data]) -> str: +def build_loader_repr_from_data(data: list[Data]) -> str: """ Builds a string representation of the loader based on the given data. @@ -422,11 +422,11 @@ def build_loader_repr_from_data(data: List[Data]) -> str: def update_settings( - config: Optional[str] = None, - cache: Optional[str] = None, + config: str | None = None, + cache: str | None = None, dev: bool = False, remove_api_keys: bool = False, - components_path: Optional[Path] = None, + components_path: Path | None = None, store: bool = True, auto_saving: bool = True, auto_saving_interval: int = 1000, diff --git a/src/backend/base/langflow/utils/validate.py b/src/backend/base/langflow/utils/validate.py index 7a0eef7b4..0ebfab2a1 100644 --- a/src/backend/base/langflow/utils/validate.py +++ b/src/backend/base/langflow/utils/validate.py @@ -2,7 +2,7 @@ import ast import contextlib import importlib from types import FunctionType -from typing import Dict, List, Optional, Union +from typing import Optional, Union from pydantic import ValidationError @@ -57,7 +57,7 @@ def validate_code(code): def eval_function(function_string: str): # Create an empty dictionary to serve as a separate namespace - namespace: Dict = {} + namespace: dict = {} # Execute the code string in the new namespace exec(function_string, namespace) @@ -276,8 +276,8 @@ def get_default_imports(code_string): default_imports = { "Optional": Optional, - "List": List, - "Dict": Dict, + "List": list, + "Dict": dict, "Union": Union, } langflow_imports = list(CUSTOM_COMPONENT_SUPPORTED_TYPES.keys()) diff --git a/src/backend/base/langflow/utils/version.py b/src/backend/base/langflow/utils/version.py index 14166ce40..bc167f2f2 100644 --- a/src/backend/base/langflow/utils/version.py +++ b/src/backend/base/langflow/utils/version.py @@ -1,5 +1,3 @@ -from typing import Optional - import httpx from langflow.logging.logger import logger @@ -70,7 +68,7 @@ def is_nightly(v: str) -> bool: return "dev" in v -def fetch_latest_version(package_name: str, include_prerelease: bool) -> Optional[str]: +def fetch_latest_version(package_name: str, include_prerelease: bool) -> str | None: from packaging import version as pkg_version package_name = package_name.replace(" ", "-").lower() diff --git a/src/backend/base/langflow/worker.py b/src/backend/base/langflow/worker.py index a5d1cf956..095b1e99a 100644 --- a/src/backend/base/langflow/worker.py +++ b/src/backend/base/langflow/worker.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union +from typing import TYPE_CHECKING, Any from asgiref.sync import async_to_sync from celery.exceptions import SoftTimeLimitExceeded # type: ignore @@ -29,9 +29,9 @@ def build_vertex(self, vertex: "Vertex") -> "Vertex": @celery_app.task(acks_late=True) def process_graph_cached_task( - data_graph: Dict[str, Any], - inputs: Optional[Union[dict, List[dict]]] = None, + data_graph: dict[str, Any], + inputs: dict | list[dict] | None = None, clear_cache=False, session_id=None, -) -> Dict[str, Any]: +) -> dict[str, Any]: raise NotImplementedError("This task is not implemented yet") diff --git a/src/backend/base/pyproject.toml b/src/backend/base/pyproject.toml index 2f4459be2..c5072c852 100644 --- a/src/backend/base/pyproject.toml +++ b/src/backend/base/pyproject.toml @@ -141,11 +141,12 @@ mypy_path = "langflow" ignore_missing_imports = true [tool.ruff] +target-version = "py310" exclude = ["langflow/alembic"] line-length = 120 [tool.ruff.lint] -select = ["E4", "E7", "E9", "F", "I"] +select = ["E4", "E7", "E9", "F", "I", "UP"] [build-system] requires = ["poetry-core"]