build: Bump ruff version to 0.9.7 (#6614)

* Bump ruff version to 0.9.7

* [autofix.ci] apply automated fixes

* refactor: Update Gmail Agent starter project to Langflow 1.2.0

This commit updates the Gmail Agent starter project with new node IDs and configuration reflecting Langflow version 1.2.0. Changes include:
- Updated node identifiers for Agent, ChatInput, ChatOutput, and ComposioAPI
- Refreshed project metadata and version information
- Minor adjustments to node configurations and viewport settings

* [autofix.ci] apply automated fixes

* fix: Revert Gmail Agent name to original

* fix: Remove trailing comma in Gmail Agent JSON

* feat: Add tags to Gmail Agent starter project

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@langflow.org>
This commit is contained in:
Christophe Bornet 2025-02-26 21:36:36 +01:00 committed by GitHub
commit d9b8211a18
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
38 changed files with 214 additions and 207 deletions

View file

@ -14,7 +14,7 @@ jobs:
- uses: actions/checkout@v4
- name: "Setup Environment"
uses: ./.github/actions/setup-uv
- run: uv run ruff check --fix-only . --ignore A005
- run: uv run ruff check --fix-only .
- run: uv run ruff format . --config pyproject.toml
- uses: autofix-ci/action@551dded8c6cc8a1054039c8bc0b8b48c51dfc6ef
- name: Minimize uv cache

View file

@ -24,6 +24,6 @@ jobs:
- name: Register problem matcher
run: echo "::add-matcher::.github/workflows/matchers/ruff.json"
- name: Run Ruff Check
run: uv run --only-dev ruff check --output-format=github . --ignore A005
run: uv run --only-dev ruff check --output-format=github .
- name: Minimize uv cache
run: uv cache prune --ci

View file

@ -198,7 +198,7 @@ fix_codespell: ## run codespell to fix spelling errors
poetry run codespell --toml pyproject.toml --write
format_backend: ## backend code formatters
@uv run ruff check . --fix --ignore EXE002 --ignore A005
@uv run ruff check . --fix
@uv run ruff format . --config pyproject.toml
format_frontend: ## frontend code formatters

View file

@ -121,7 +121,7 @@ dev = [
"types-redis>=4.6.0.5",
"ipykernel>=6.29.0",
"mypy>=1.11.0",
"ruff>=0.9.1,<0.10",
"ruff>=0.9.7,<0.10",
"httpx>=0.27.0",
"pytest>=8.2.0",
"types-requests>=2.32.0",

View file

@ -580,14 +580,14 @@ async def experimental_run_flow(
@router.post(
"/predict/{flow_id}",
"/predict/{_flow_id}",
dependencies=[Depends(api_key_security)],
)
@router.post(
"/process/{flow_id}",
"/process/{_flow_id}",
dependencies=[Depends(api_key_security)],
)
async def process() -> None:
async def process(_flow_id) -> None:
"""Endpoint to process an input with a given flow_id."""
# Raise a depreciation warning
logger.warning(

View file

@ -0,0 +1 @@
# noqa: A005

View file

@ -89,9 +89,9 @@ class ApifyActorsComponent(Component):
def run_model(self) -> list[Data]:
"""Run the Actor and return node output."""
_input = json.loads(self.run_input)
input_ = json.loads(self.run_input)
fields = ApifyActorsComponent.parse_dataset_fields(self.dataset_fields) if self.dataset_fields else None
res = self._run_actor(self.actor_id, _input, fields=fields)
res = self._run_actor(self.actor_id, input_, fields=fields)
if self.flatten_dataset:
res = [ApifyActorsComponent.flatten(item) for item in res]
data = [Data(data=item) for item in res]
@ -113,16 +113,16 @@ class ApifyActorsComponent(Component):
properties = {"run_input": properties}
# works from input schema
_info = [
info_ = [
(
"JSON encoded as a string with input schema (STRICTLY FOLLOW JSON FORMAT AND SCHEMA):\n\n"
f"{json.dumps(properties, separators=(',', ':'))}"
)
]
if required:
_info.append("\n\nRequired fields:\n" + "\n".join(required))
info_.append("\n\nRequired fields:\n" + "\n".join(required))
info = "".join(_info)
info = "".join(info_)
input_model_cls = ApifyActorsComponent.create_input_model_class(info)
tool_cls = ApifyActorsComponent.create_tool_class(self, readme, input_model_cls, actor_id)

View file

@ -158,7 +158,7 @@ class ComposioAPIComponent(LCToolComponent):
for item in data.get("items", []):
for auth_scheme in item.get("auth_schemes", []):
if auth_scheme.get("mode") in ["OAUTH1", "OAUTH2"]:
if auth_scheme.get("mode") in {"OAUTH1", "OAUTH2"}:
oauth_apps.append(item["key"].upper())
break
except requests.RequestException as e:

View file

@ -321,7 +321,7 @@ class APIRequestComponent(Component):
elif field_name == "curl":
field_config["advanced"] = not use_curl
field_config["real_time_refresh"] = use_curl
elif field_name in ["body", "headers"]:
elif field_name in {"body", "headers"}:
field_config["advanced"] = True # Always keep body and headers in advanced when use_curl is False
else:
field_config["advanced"] = use_curl
@ -359,7 +359,7 @@ class APIRequestComponent(Component):
if field_name in common_fields:
field_config["advanced"] = False
elif field_name in body_fields:
field_config["advanced"] = method not in ["POST", "PUT", "PATCH"]
field_config["advanced"] = method not in {"POST", "PUT", "PATCH"}
elif field_name in always_advanced_fields:
field_config["advanced"] = True
else:

View file

@ -1,5 +1,6 @@
from __future__ import annotations
import operator
from typing import TYPE_CHECKING, Any
from loguru import logger
@ -165,7 +166,7 @@ class BatchRunComponent(Component):
]
# Sort by index to maintain order
responses_with_idx.sort(key=lambda x: x[0])
responses_with_idx.sort(key=operator.itemgetter(0))
# Build the final data with enhanced metadata
rows: list[dict[str, Any]] = []

View file

@ -138,7 +138,7 @@ class AnthropicModelComponent(LCModelComponent):
return None
def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None):
if field_name in ("base_url", "model_name", "tool_model_enabled", "api_key") and field_value:
if field_name in {"base_url", "model_name", "tool_model_enabled", "api_key"} and field_value:
try:
if len(self.api_key) == 0:
ids = ANTHROPIC_MODELS

View file

@ -129,7 +129,7 @@ class GoogleGenerativeAIComponent(LCModelComponent):
return model_ids
def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None):
if field_name in ("base_url", "model_name", "tool_model_enabled", "api_key") and field_value:
if field_name in {"base_url", "model_name", "tool_model_enabled", "api_key"} and field_value:
try:
if len(self.api_key) == 0:
ids = GOOGLE_GENERATIVE_AI_MODELS

View file

@ -105,7 +105,7 @@ class GroqModel(LCModelComponent):
return model_ids
def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None):
if field_name in ("base_url", "model_name", "tool_model_enabled", "api_key") and field_value:
if field_name in {"base_url", "model_name", "tool_model_enabled", "api_key"} and field_value:
try:
if len(self.api_key) != 0:
try:

View file

@ -78,7 +78,7 @@ class NVIDIAModelComponent(LCModelComponent):
return [model.id for model in build_model.available_models]
def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None):
if field_name in ("base_url", "model_name", "tool_model_enabled", "api_key") and field_value:
if field_name in {"base_url", "model_name", "tool_model_enabled", "api_key"} and field_value:
try:
ids = self.get_models(self.tool_model_enabled)
build_config["model_name"]["options"] = ids

View file

@ -231,5 +231,5 @@ class NvidiaIngestComponent(Component):
# image is not yet supported; skip if encountered
self.log(f"Unsupported document type: {document_type}", name="NVIDIAIngestComponent")
self.status = data if data else "No data"
self.status = data or "No data"
return data

View file

@ -87,7 +87,7 @@ class SaveToFileComponent(Component):
build_config["data"]["show"] = field_value == "Data"
build_config["message"]["show"] = field_value == "Message"
if field_value in ["DataFrame", "Data"]:
if field_value in {"DataFrame", "Data"}:
build_config["file_format"]["options"] = self.DATA_FORMAT_CHOICES
elif field_value == "Message":
build_config["file_format"]["options"] = self.MESSAGE_FORMAT_CHOICES

View file

@ -112,7 +112,7 @@ class ArXivComponent(Component):
# Validate URL scheme and host
parsed_url = urlparse(url)
if parsed_url.scheme not in ("http", "https"):
if parsed_url.scheme not in {"http", "https"}:
error_msg = f"Invalid URL scheme: {parsed_url.scheme}"
raise ValueError(error_msg)
if parsed_url.hostname != "export.arxiv.org":

View file

@ -571,7 +571,7 @@ class AstraDBVectorStoreComponent(LCVectorStoreComponent):
# Go over each possible provider and add metadata to configure in Astra DB Portal
for provider in provider_options:
# Skip Bring your own and Nvidia, automatically configured
if provider in ["Bring your own", "Nvidia"]:
if provider in {"Bring your own", "Nvidia"}:
build_config["collection_name"]["dialog_inputs"]["fields"]["data"]["node"]["template"][
"embedding_generation_provider"
]["options_metadata"].append({"icon": self.get_provider_icon(provider_name=provider.lower())})
@ -601,7 +601,7 @@ class AstraDBVectorStoreComponent(LCVectorStoreComponent):
# If we retrieved options based on the token, show the dropdown
build_config["collection_name"]["options"] = [col["name"] for col in collection_options]
build_config["collection_name"]["options_metadata"] = [
{k: v for k, v in col.items() if k not in ["name"]} for col in collection_options
{k: v for k, v in col.items() if k != "name"} for col in collection_options
]
# Reset the selected collection
@ -620,7 +620,7 @@ class AstraDBVectorStoreComponent(LCVectorStoreComponent):
# If we retrieved options based on the token, show the dropdown
build_config["database_name"]["options"] = [db["name"] for db in database_options]
build_config["database_name"]["options_metadata"] = [
{k: v for k, v in db.items() if k not in ["name"]} for db in database_options
{k: v for k, v in db.items() if k != "name"} for db in database_options
]
# Reset the selected database
@ -667,12 +667,8 @@ class AstraDBVectorStoreComponent(LCVectorStoreComponent):
raise ValueError(msg) from e
# Add the new database to the list of options
build_config["database_name"]["options"] = build_config["database_name"]["options"] + [
field_value["new_database_name"]
]
build_config["database_name"]["options_metadata"] = build_config["database_name"]["options_metadata"] + [
{"status": "PENDING"}
]
build_config["database_name"]["options"] += [field_value["new_database_name"]]
build_config["database_name"]["options_metadata"] += [{"status": "PENDING"}]
return self.reset_collection_list(build_config)
@ -726,9 +722,9 @@ class AstraDBVectorStoreComponent(LCVectorStoreComponent):
# Add the new collection to the list of options
icon = "NVIDIA" if provider == "Nvidia" else "vectorstores"
build_config["collection_name"]["options_metadata"] = build_config["collection_name"][
"options_metadata"
] + [{"records": 0, "provider": provider, "icon": icon, "model": model}]
build_config["collection_name"]["options_metadata"] += [
{"records": 0, "provider": provider, "icon": icon, "model": model}
]
return build_config
@ -748,7 +744,7 @@ class AstraDBVectorStoreComponent(LCVectorStoreComponent):
return self.reset_build_config(build_config)
# If this is the first execution of the component, reset and build database list
if first_run or field_name in ["token", "environment"]:
if first_run or field_name in {"token", "environment"}:
return self.reset_database_list(build_config)
# Refresh the collection name options
@ -790,7 +786,12 @@ class AstraDBVectorStoreComponent(LCVectorStoreComponent):
# Add the new collection to the list of options
build_config["collection_name"]["options"].append(field_value)
build_config["collection_name"]["options_metadata"].append(
{"records": 0, "provider": None, "icon": "", "model": None}
{
"records": 0,
"provider": None,
"icon": "",
"model": None,
}
)
# Ensure that autodetect collection is set to False, since its a new collection

View file

@ -230,7 +230,7 @@ class YouTubeVideoDetailsComponent(Component):
thumb_cols = [col for col in video_df.columns if col.startswith("thumbnail_")]
# Reorder columns based on what's included
ordered_cols = basic_cols[:]
ordered_cols = basic_cols.copy()
if self.include_statistics:
ordered_cols.extend([col for col in stat_cols if col in video_df.columns])

View file

@ -518,7 +518,7 @@ def layered_topological_sort(
layers: list[list[str]] = []
visited = set()
cycle_counts = {vertex: 0 for vertex in vertices_ids}
cycle_counts = dict.fromkeys(vertices_ids, 0)
current_layer = 0
# Process the first layer separately to avoid duplicates

View file

@ -322,7 +322,7 @@ def json_schema_from_flow(flow: Flow) -> dict:
from langflow.graph.graph.base import Graph
# Get the flow's data which contains the nodes and their configurations
flow_data = flow.data if flow.data else {}
flow_data = flow.data or {}
graph = Graph.from_payload(flow_data)
input_nodes = [vertex for vertex in graph.vertices if vertex.is_input]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,3 +1,4 @@
# noqa: A005
from langflow.inputs import (
BoolInput,
CodeInput,

View file

@ -1,3 +1,4 @@
# noqa: A005
from .logger import configure, logger
from .setup import disable_logging, enable_logging

View file

@ -145,7 +145,7 @@ def _serialize_numpy_type(obj: Any, max_length: int | None, max_items: int | Non
if np.issubdtype(obj.dtype, np.bool_):
return bool(obj)
if np.issubdtype(obj.dtype, np.complexfloating):
return complex(cast(complex, obj))
return complex(cast("complex", obj))
if np.issubdtype(obj.dtype, np.str_):
return _serialize_str(str(obj), max_length, max_items)
if np.issubdtype(obj.dtype, np.bytes_) and hasattr(obj, "tobytes"):
@ -209,7 +209,7 @@ def _serialize_dispatcher(obj: Any, max_length: int | None, max_items: int | Non
if np.issubdtype(obj.dtype, np.bool_):
return bool(obj)
if np.issubdtype(obj.dtype, np.complexfloating):
return complex(cast(complex, obj))
return complex(cast("complex", obj))
if np.issubdtype(obj.dtype, np.str_):
return str(obj)
if np.issubdtype(obj.dtype, np.bytes_) and hasattr(obj, "tobytes"):

View file

@ -203,7 +203,7 @@ class JobQueueService(Service):
return
logger.info(f"Commencing cleanup for job_id {job_id}")
main_queue, event_manager, task = self._queues[job_id]
main_queue, _event_manager, task = self._queues[job_id]
# Cancel the associated task if it is still running.
if task and not task.done():

View file

@ -0,0 +1 @@
# noqa: A005

View file

@ -93,7 +93,7 @@ class TelemetryService(Service):
def _get_langflow_desktop(self) -> bool:
# Coerce to bool, could be 1, 0, True, False, "1", "0", "True", "False"
return str(os.getenv("LANGFLOW_DESKTOP", "False")).lower() in ("1", "true")
return str(os.getenv("LANGFLOW_DESKTOP", "False")).lower() in {"1", "true"}
async def log_package_version(self) -> None:
python_version = ".".join(platform.python_version().split(".")[:2])

View file

@ -57,7 +57,7 @@ async def test_update_component_outputs(client: AsyncClient, logged_in_headers:
async def test_update_component_model_name_options(client: AsyncClient, logged_in_headers: dict):
"""Test that model_name options are updated when selecting a provider."""
component = AgentComponent()
component_node, cc_instance = build_custom_component_template(
component_node, _cc_instance = build_custom_component_template(
component,
)

View file

@ -271,9 +271,9 @@ class TestChromaVectorStoreComponent(ComponentTestBaseWithoutClient):
assert isinstance(data_obj, Data)
assert "id" in data_obj.data
assert "text" in data_obj.data
assert data_obj.data["text"] in ["Document 1", "Document 2"]
assert data_obj.data["text"] in {"Document 1", "Document 2"}
assert "metadata_field" in data_obj.data
assert data_obj.data["metadata_field"] in ["value1", "value2"]
assert data_obj.data["metadata_field"] in {"value1", "value2"}
def test_chroma_collection_to_data_without_metadata(
self, component_class: type[ChromaVectorStoreComponent], default_kwargs: dict[str, Any]
@ -300,7 +300,7 @@ class TestChromaVectorStoreComponent(ComponentTestBaseWithoutClient):
assert isinstance(data_obj, Data)
assert "id" in data_obj.data
assert "text" in data_obj.data
assert data_obj.data["text"] in ["Simple document 1", "Simple document 2"]
assert data_obj.data["text"] in {"Simple document 1", "Simple document 2"}
def test_chroma_collection_to_data_empty_collection(
self, component_class: type[ChromaVectorStoreComponent], default_kwargs: dict[str, Any]

View file

@ -869,7 +869,7 @@ def test_get_sorted_vertices_with_unconnected_graph():
predecessor_map = {vertex: data["predecessors"] for vertex, data in graph_dict.items()}
def is_input_vertex(vertex_id: str) -> bool:
return vertex_id in ["A"]
return vertex_id == "A"
def get_vertex_predecessors(vertex_id: str) -> list[str]:
return predecessor_map[vertex_id]

View file

@ -263,13 +263,13 @@ class TestSerializationHypothesis:
assert isinstance(serialize(np.uint64(42)), int)
# Test floats
assert serialize(np.float64(3.14)) == 3.14
assert isinstance(serialize(np.float64(3.14)), float)
assert serialize(np.float64(math.pi)) == math.pi
assert isinstance(serialize(np.float64(math.pi)), float)
# Test float32 (need to account for precision differences)
float32_val = serialize(np.float32(3.14))
float32_val = serialize(np.float32(math.pi))
assert isinstance(float32_val, float)
assert abs(float32_val - 3.14) < 1e-6 # Check if close enough
assert abs(float32_val - math.pi) < 1e-6 # Check if close enough
# Test bool
assert serialize(np.bool_(True)) is True # noqa: FBT003

2
uv.lock generated
View file

@ -4544,7 +4544,7 @@ dev = [
{ name = "pytest-xdist", specifier = ">=3.6.0" },
{ name = "requests", specifier = ">=2.32.0" },
{ name = "respx", specifier = ">=0.21.1" },
{ name = "ruff", specifier = ">=0.9.1,<0.10" },
{ name = "ruff", specifier = ">=0.9.7,<0.10" },
{ name = "types-aiofiles", specifier = ">=24.1.0.20240626" },
{ name = "types-google-cloud-ndb", specifier = ">=2.2.0.0" },
{ name = "types-markdown", specifier = ">=3.7.0.20240822" },