diff --git a/pyproject.toml b/pyproject.toml index 472d13dbf..674f8f510 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -154,7 +154,7 @@ dev-dependencies = [ "types-redis>=4.6.0.5", "ipykernel>=6.29.0", "mypy>=1.11.0", - "ruff>=0.8.4,<0.9.0", + "ruff>=0.9.1,<0.10", "httpx>=0.27.0", "pytest>=8.2.0", "types-requests>=2.32.0", diff --git a/scripts/ci/update_starter_projects.py b/scripts/ci/update_starter_projects.py index 807df6fd7..b001ebfc8 100644 --- a/scripts/ci/update_starter_projects.py +++ b/scripts/ci/update_starter_projects.py @@ -11,7 +11,7 @@ from langflow.initial_setup.setup import ( update_project_file, update_projects_components_with_latest_component_versions, ) -from langflow.interface.types import get_and_cache_all_types_dict +from langflow.interface.components import get_and_cache_all_types_dict from langflow.services.deps import get_settings_service from langflow.services.utils import initialize_services diff --git a/src/backend/base/langflow/api/v1/chat.py b/src/backend/base/langflow/api/v1/chat.py index ad8f1cd7e..1cbecb268 100644 --- a/src/backend/base/langflow/api/v1/chat.py +++ b/src/backend/base/langflow/api/v1/chat.py @@ -48,7 +48,7 @@ from langflow.services.deps import get_chat_service, get_session, get_telemetry_ from langflow.services.telemetry.schema import ComponentPayload, PlaygroundPayload if TYPE_CHECKING: - from langflow.graph.vertex.types import InterfaceVertex + from langflow.graph.vertex.vertex_types import InterfaceVertex router = APIRouter(tags=["Chat"]) diff --git a/src/backend/base/langflow/api/v1/endpoints.py b/src/backend/base/langflow/api/v1/endpoints.py index f118cdaea..47e20e808 100644 --- a/src/backend/base/langflow/api/v1/endpoints.py +++ b/src/backend/base/langflow/api/v1/endpoints.py @@ -58,7 +58,7 @@ router = APIRouter(tags=["Base"]) @router.get("/all", dependencies=[Depends(get_current_active_user)]) async def get_all(): - from langflow.interface.types import get_and_cache_all_types_dict + from langflow.interface.components import get_and_cache_all_types_dict try: return await get_and_cache_all_types_dict(settings_service=get_settings_service()) diff --git a/src/backend/base/langflow/components/langchain_utilities/__init__.py b/src/backend/base/langflow/components/langchain_utilities/__init__.py index 9097ed492..9fd9bb331 100644 --- a/src/backend/base/langflow/components/langchain_utilities/__init__.py +++ b/src/backend/base/langflow/components/langchain_utilities/__init__.py @@ -1,9 +1,9 @@ from .character import CharacterTextSplitterComponent from .conversation import ConversationChainComponent -from .csv import CSVAgentComponent +from .csv_agent import CSVAgentComponent from .fake_embeddings import FakeEmbeddingsComponent from .html_link_extractor import HtmlLinkExtractorComponent -from .json import JsonAgentComponent +from .json_agent import JsonAgentComponent from .json_document_builder import JSONDocumentBuilder from .langchain_hub import LangChainHubPromptComponent from .language_recursive import LanguageRecursiveTextSplitterComponent @@ -26,7 +26,7 @@ from .tool_calling import ToolCallingAgentComponent from .vector_store import VectoStoreRetrieverComponent from .vector_store_info import VectorStoreInfoComponent from .vector_store_router import VectorStoreRouterAgentComponent -from .xml import XMLAgentComponent +from .xml_agent import XMLAgentComponent __all__ = [ "CSVAgentComponent", diff --git a/src/backend/base/langflow/components/langchain_utilities/csv.py b/src/backend/base/langflow/components/langchain_utilities/csv_agent.py similarity index 100% rename from src/backend/base/langflow/components/langchain_utilities/csv.py rename to src/backend/base/langflow/components/langchain_utilities/csv_agent.py diff --git a/src/backend/base/langflow/components/langchain_utilities/json.py b/src/backend/base/langflow/components/langchain_utilities/json_agent.py similarity index 100% rename from src/backend/base/langflow/components/langchain_utilities/json.py rename to src/backend/base/langflow/components/langchain_utilities/json_agent.py diff --git a/src/backend/base/langflow/components/langchain_utilities/xml.py b/src/backend/base/langflow/components/langchain_utilities/xml_agent.py similarity index 100% rename from src/backend/base/langflow/components/langchain_utilities/xml.py rename to src/backend/base/langflow/components/langchain_utilities/xml_agent.py diff --git a/src/backend/base/langflow/graph/__init__.py b/src/backend/base/langflow/graph/__init__.py index 741f7468c..d68fd432b 100644 --- a/src/backend/base/langflow/graph/__init__.py +++ b/src/backend/base/langflow/graph/__init__.py @@ -1,6 +1,6 @@ from langflow.graph.edge.base import Edge from langflow.graph.graph.base import Graph from langflow.graph.vertex.base import Vertex -from langflow.graph.vertex.types import CustomComponentVertex, InterfaceVertex, StateVertex +from langflow.graph.vertex.vertex_types import CustomComponentVertex, InterfaceVertex, StateVertex __all__ = ["CustomComponentVertex", "Edge", "Graph", "InterfaceVertex", "StateVertex", "Vertex"] diff --git a/src/backend/base/langflow/graph/graph/base.py b/src/backend/base/langflow/graph/graph/base.py index 366089195..9bc07e703 100644 --- a/src/backend/base/langflow/graph/graph/base.py +++ b/src/backend/base/langflow/graph/graph/base.py @@ -33,7 +33,7 @@ from langflow.graph.graph.utils import ( from langflow.graph.schema import InterfaceComponentTypes, RunOutputs from langflow.graph.vertex.base import Vertex, VertexStates from langflow.graph.vertex.schema import NodeData, NodeTypeEnum -from langflow.graph.vertex.types import ComponentVertex, InterfaceVertex, StateVertex +from langflow.graph.vertex.vertex_types import ComponentVertex, InterfaceVertex, StateVertex from langflow.logging.logger import LogConfig, configure from langflow.schema.dotdict import dotdict from langflow.schema.schema import INPUT_FIELD_NAME, InputType diff --git a/src/backend/base/langflow/graph/graph/constants.py b/src/backend/base/langflow/graph/graph/constants.py index a3c0bf85c..fd9306d9d 100644 --- a/src/backend/base/langflow/graph/graph/constants.py +++ b/src/backend/base/langflow/graph/graph/constants.py @@ -11,9 +11,9 @@ class Finish: def _import_vertex_types(): - from langflow.graph.vertex import types + from langflow.graph.vertex import vertex_types - return types + return vertex_types class VertexTypesDict(LazyLoadDictBase): diff --git a/src/backend/base/langflow/graph/vertex/types.py b/src/backend/base/langflow/graph/vertex/vertex_types.py similarity index 100% rename from src/backend/base/langflow/graph/vertex/types.py rename to src/backend/base/langflow/graph/vertex/vertex_types.py diff --git a/src/backend/base/langflow/interface/types.py b/src/backend/base/langflow/interface/components.py similarity index 100% rename from src/backend/base/langflow/interface/types.py rename to src/backend/base/langflow/interface/components.py diff --git a/src/backend/base/langflow/interface/listing.py b/src/backend/base/langflow/interface/listing.py index de87df1c9..cb6853220 100644 --- a/src/backend/base/langflow/interface/listing.py +++ b/src/backend/base/langflow/interface/listing.py @@ -17,7 +17,7 @@ class AllTypesDict(LazyLoadDictBase): @override def get_type_dict(self): - from langflow.interface.types import get_all_types_dict + from langflow.interface.components import get_all_types_dict settings_service = get_settings_service() return get_all_types_dict(settings_service.settings.components_path) diff --git a/src/backend/base/langflow/main.py b/src/backend/base/langflow/main.py index e102ac940..179d1751f 100644 --- a/src/backend/base/langflow/main.py +++ b/src/backend/base/langflow/main.py @@ -29,7 +29,7 @@ from langflow.initial_setup.setup import ( load_bundles_from_urls, load_flows_from_directory, ) -from langflow.interface.types import get_and_cache_all_types_dict +from langflow.interface.components import get_and_cache_all_types_dict from langflow.interface.utils import setup_llm_caching from langflow.logging.logger import configure from langflow.middleware import ContentSizeLimitMiddleware diff --git a/src/backend/base/langflow/utils/async_helpers.py b/src/backend/base/langflow/utils/async_helpers.py index 68b68e147..60628286c 100644 --- a/src/backend/base/langflow/utils/async_helpers.py +++ b/src/backend/base/langflow/utils/async_helpers.py @@ -7,6 +7,7 @@ if hasattr(asyncio, "timeout"): async def timeout_context(timeout_seconds): with asyncio.timeout(timeout_seconds) as ctx: yield ctx + else: @asynccontextmanager diff --git a/src/backend/tests/performance/test_server_init.py b/src/backend/tests/performance/test_server_init.py index 42f7e15a3..73f72792d 100644 --- a/src/backend/tests/performance/test_server_init.py +++ b/src/backend/tests/performance/test_server_init.py @@ -58,7 +58,7 @@ async def test_initialize_super_user(): @pytest.mark.benchmark async def test_get_and_cache_all_types_dict(): """Benchmark get_and_cache_all_types_dict function.""" - from langflow.interface.types import get_and_cache_all_types_dict + from langflow.interface.components import get_and_cache_all_types_dict settings_service = get_settings_service() result = await get_and_cache_all_types_dict(settings_service) @@ -70,7 +70,7 @@ async def test_get_and_cache_all_types_dict(): async def test_create_starter_projects(): """Benchmark creation of starter projects.""" from langflow.initial_setup.setup import create_or_update_starter_projects - from langflow.interface.types import get_and_cache_all_types_dict + from langflow.interface.components import get_and_cache_all_types_dict from langflow.services.utils import initialize_services await initialize_services(fix_migration=False) diff --git a/src/backend/tests/unit/base/tools/test_toolmodemixin.py b/src/backend/tests/unit/base/tools/test_toolmodemixin.py index 8e68ef6af..27518360c 100644 --- a/src/backend/tests/unit/base/tools/test_toolmodemixin.py +++ b/src/backend/tests/unit/base/tools/test_toolmodemixin.py @@ -150,6 +150,6 @@ def test_component_inputs_toolkit(): for input_name, expected in expected_inputs.items(): assert input_name in properties, f"{input_name} is missing in properties." assert properties[input_name]["title"] == expected["title"], f"Title mismatch for {input_name}." - assert ( - properties[input_name]["description"] == expected["description"] - ), f"Description mismatch for {input_name}." + assert properties[input_name]["description"] == expected["description"], ( + f"Description mismatch for {input_name}." + ) diff --git a/src/backend/tests/unit/components/data/test_api_request_component.py b/src/backend/tests/unit/components/data/test_api_request_component.py index b4d21e43a..cb134eefa 100644 --- a/src/backend/tests/unit/components/data/test_api_request_component.py +++ b/src/backend/tests/unit/components/data/test_api_request_component.py @@ -78,9 +78,9 @@ async def test_httpx_metadata_behavior(api_request, include_metadata, expected_p assert metadata["response_headers"]["custom-header"] == "HeaderValue" # Validate redirection history - assert metadata["redirection_history"] == [ - {"url": redirected_url, "status_code": 303} - ], "Redirection history is incorrect" + assert metadata["redirection_history"] == [{"url": redirected_url, "status_code": 303}], ( + "Redirection history is incorrect" + ) # Validate result assert metadata["result"] == response_content, "Response content mismatch" @@ -111,9 +111,9 @@ async def test_save_to_file_behavior(api_request, save_to_file, expected_propert # Check returned metadata metadata = result.data - assert ( - set(metadata.keys()) == expected_properties - ), f"Unexpected properties: {set(metadata.keys())}. Raw result: {result.data}" + assert set(metadata.keys()) == expected_properties, ( + f"Unexpected properties: {set(metadata.keys())}. Raw result: {result.data}" + ) if save_to_file: # Validate that file_path exists in metadata diff --git a/src/backend/tests/unit/components/data/test_directory_component.py b/src/backend/tests/unit/components/data/test_directory_component.py index 3afd64ef1..b08ae2d77 100644 --- a/src/backend/tests/unit/components/data/test_directory_component.py +++ b/src/backend/tests/unit/components/data/test_directory_component.py @@ -159,9 +159,9 @@ class TestDirectoryComponent(ComponentTestBaseWithoutClient): # Check column names expected_columns = ["text", "file_path"] actual_columns = list(data_frame.columns) - assert set(expected_columns).issubset( - set(actual_columns) - ), f"Missing required columns. Expected at least {expected_columns}, got {actual_columns}" + assert set(expected_columns).issubset(set(actual_columns)), ( + f"Missing required columns. Expected at least {expected_columns}, got {actual_columns}" + ) # Verify content matches input files texts = data_frame["text"].tolist() @@ -260,9 +260,9 @@ class TestDirectoryComponent(ComponentTestBaseWithoutClient): results = directory_component.load_directory() # Verify number of loaded files - assert ( - len(results) == expected_count - ), f"Expected {expected_count} results for file types {file_types}, got {len(results)}" + assert len(results) == expected_count, ( + f"Expected {expected_count} results for file types {file_types}, got {len(results)}" + ) # Optionally, check the file extension in each result for r in results: # e.g., verify that the extension is indeed in file_types @@ -358,20 +358,20 @@ class TestDirectoryComponent(ComponentTestBaseWithoutClient): # Verify parallel_load_data was called with correct parameters mock_parallel_load.assert_called_once() call_args = mock_parallel_load.call_args[1] - assert ( - call_args["max_concurrency"] == 2 - ), f"Expected max_concurrency=2, got {call_args.get('max_concurrency')}" - assert ( - call_args["silent_errors"] is False - ), f"Expected silent_errors=False, got {call_args.get('silent_errors')}" + assert call_args["max_concurrency"] == 2, ( + f"Expected max_concurrency=2, got {call_args.get('max_concurrency')}" + ) + assert call_args["silent_errors"] is False, ( + f"Expected silent_errors=False, got {call_args.get('silent_errors')}" + ) # Verify results - assert ( - len(results) == 2 - ), f"Expected 2 results, got {len(results)}: {[r.data['file_path'] for r in results]}" - assert all( - isinstance(r, Data) for r in results - ), f"All results should be Data objects, got types: {[type(r) for r in results]}" + assert len(results) == 2, ( + f"Expected 2 results, got {len(results)}: {[r.data['file_path'] for r in results]}" + ) + assert all(isinstance(r, Data) for r in results), ( + f"All results should be Data objects, got types: {[type(r) for r in results]}" + ) actual_texts = [r.text for r in results] expected_texts = ["content1", "content2"] diff --git a/src/backend/tests/unit/components/data/test_url_component.py b/src/backend/tests/unit/components/data/test_url_component.py index f78eaadd5..d3fc8e056 100644 --- a/src/backend/tests/unit/components/data/test_url_component.py +++ b/src/backend/tests/unit/components/data/test_url_component.py @@ -121,7 +121,7 @@ class TestURLComponent(ComponentTestBaseWithoutClient): component.set_attributes({"urls": urls}) mock_web_load.return_value = [ - Mock(page_content=f"content{i+1}", metadata={"source": url}) for i, url in enumerate(urls) + Mock(page_content=f"content{i + 1}", metadata={"source": url}) for i, url in enumerate(urls) ] # Test fetch_content @@ -131,7 +131,7 @@ class TestURLComponent(ComponentTestBaseWithoutClient): for i, item in enumerate(content): url = urls[i] assert item.source == url, f"Expected '{url}', got '{item.source}'" - assert item.text == f"content{i+1}" + assert item.text == f"content{i + 1}" @respx.mock async def test_url_request_success(self, mock_web_load): diff --git a/src/backend/tests/unit/components/models/test_huggingface.py b/src/backend/tests/unit/components/models/test_huggingface.py index 3b1ceb659..e3e3073c8 100644 --- a/src/backend/tests/unit/components/models/test_huggingface.py +++ b/src/backend/tests/unit/components/models/test_huggingface.py @@ -24,6 +24,6 @@ def test_huggingface_inputs(): # Check if all expected inputs are present for name, input_type in expected_inputs.items(): - assert any( - isinstance(inp, input_type) and inp.name == name for inp in inputs - ), f"Missing or incorrect input: {name}" + assert any(isinstance(inp, input_type) and inp.name == name for inp in inputs), ( + f"Missing or incorrect input: {name}" + ) diff --git a/src/backend/tests/unit/components/processing/test_split_text_component.py b/src/backend/tests/unit/components/processing/test_split_text_component.py index a477a40fd..bd2b574ef 100644 --- a/src/backend/tests/unit/components/processing/test_split_text_component.py +++ b/src/backend/tests/unit/components/processing/test_split_text_component.py @@ -54,9 +54,9 @@ class TestSplitTextComponent(ComponentTestBaseWithoutClient): assert len(results) == 3, f"Expected 3 chunks, got {len(results)}" assert "This is a test" in results[0].text, f"Expected 'This is a test', got '{results[0].text}'" assert "It has multiple lines" in results[1].text, f"Expected 'It has multiple lines', got '{results[1].text}'" - assert ( - "Each line should be a chunk" in results[2].text - ), f"Expected 'Each line should be a chunk', got '{results[2].text}'" + assert "Each line should be a chunk" in results[2].text, ( + f"Expected 'Each line should be a chunk', got '{results[2].text}'" + ) def test_split_text_with_overlap(self): """Test text splitting with overlap.""" @@ -123,12 +123,12 @@ class TestSplitTextComponent(ComponentTestBaseWithoutClient): results = component.split_text() assert len(results) == 2, f"Expected 2 chunks, got {len(results)}" for result in results: - assert ( - result.data["source"] == test_metadata["source"] - ), f"Expected source '{test_metadata['source']}', got '{result.data.get('source')}'" - assert ( - result.data["author"] == test_metadata["author"] - ), f"Expected author '{test_metadata['author']}', got '{result.data.get('author')}'" + assert result.data["source"] == test_metadata["source"], ( + f"Expected source '{test_metadata['source']}', got '{result.data.get('source')}'" + ) + assert result.data["author"] == test_metadata["author"], ( + f"Expected author '{test_metadata['author']}', got '{result.data.get('author')}'" + ) def test_split_text_as_dataframe(self): """Test converting split text results to DataFrame.""" @@ -150,15 +150,15 @@ class TestSplitTextComponent(ComponentTestBaseWithoutClient): assert isinstance(data_frame, DataFrame), "Expected DataFrame instance" assert len(data_frame) == 3, f"Expected DataFrame with 3 rows, got {len(data_frame)}" assert list(data_frame.columns) == ["text"], f"Expected columns ['text'], got {list(data_frame.columns)}" - assert ( - "First chunk" in data_frame.iloc[0]["text"] - ), f"Expected 'First chunk', got '{data_frame.iloc[0]['text']}'" - assert ( - "Second chunk" in data_frame.iloc[1]["text"] - ), f"Expected 'Second chunk', got '{data_frame.iloc[1]['text']}'" - assert ( - "Third chunk" in data_frame.iloc[2]["text"] - ), f"Expected 'Third chunk', got '{data_frame.iloc[2]['text']}'" + assert "First chunk" in data_frame.iloc[0]["text"], ( + f"Expected 'First chunk', got '{data_frame.iloc[0]['text']}'" + ) + assert "Second chunk" in data_frame.iloc[1]["text"], ( + f"Expected 'Second chunk', got '{data_frame.iloc[1]['text']}'" + ) + assert "Third chunk" in data_frame.iloc[2]["text"], ( + f"Expected 'Third chunk', got '{data_frame.iloc[2]['text']}'" + ) def test_split_text_empty_input(self): """Test handling of empty input text.""" diff --git a/src/backend/tests/unit/graph/graph/test_utils.py b/src/backend/tests/unit/graph/graph/test_utils.py index 49f2155a4..9293dfe74 100644 --- a/src/backend/tests/unit/graph/graph/test_utils.py +++ b/src/backend/tests/unit/graph/graph/test_utils.py @@ -672,13 +672,13 @@ def test_get_sorted_vertices_with_complex_cycle(graph_with_loop): # When is_cyclic is True and start_vertex_id is provided: # 1. The first layer will contain vertices with no predecessors and vertices that are part of the cycle # 2. This is because the cycle vertices are treated as having no dependencies in the initial sort - assert ( - "OpenAI Embeddings" in first_layer - ), "Vertex with no predecessors 'OpenAI Embeddings' should be in first layer" + assert "OpenAI Embeddings" in first_layer, ( + "Vertex with no predecessors 'OpenAI Embeddings' should be in first layer" + ) assert "Playlist Extractor" in first_layer, "Input vertex 'Playlist Extractor' should be in first layer" - assert ( - len(first_layer) == 2 - ), f"First layer should contain exactly 4 vertices, got {len(first_layer)}: {first_layer}" + assert len(first_layer) == 2, ( + f"First layer should contain exactly 4 vertices, got {len(first_layer)}: {first_layer}" + ) # Verify that the remaining layers contain the rest of the vertices in the correct order # The graph structure shows: @@ -747,14 +747,14 @@ def test_get_sorted_vertices_with_stop_at_chroma(graph_with_loop): # When is_cyclic is True and we have a stop component: # 1. The first layer will contain vertices with no predecessors and vertices that are part of the cycle # 2. This is because the cycle vertices are treated as having no dependencies in the initial sort - assert ( - "OpenAI Embeddings" in first_layer - ), "Vertex with no predecessors 'OpenAI Embeddings' should be in first layer" + assert "OpenAI Embeddings" in first_layer, ( + "Vertex with no predecessors 'OpenAI Embeddings' should be in first layer" + ) assert "Playlist Extractor" in first_layer, "Input vertex 'Playlist Extractor' should be in first layer" - assert ( - len(first_layer) == 2 - ), f"First layer should contain exactly 4 vertices, got {len(first_layer)}: {first_layer}" + assert len(first_layer) == 2, ( + f"First layer should contain exactly 4 vertices, got {len(first_layer)}: {first_layer}" + ) # Verify that the remaining layers contain the rest of the vertices in the correct order # The graph structure shows: @@ -848,5 +848,5 @@ def test_get_sorted_vertices_exact_sequence(graph_with_loop): assert sequence == expected_sequence, f"Sequence: {sequence}" # Verify the exact sequence assert len(sequence) == len(expected_sequence), ( - f"Expected sequence length {len(expected_sequence)}, " f"but got {len(sequence)}" + f"Expected sequence length {len(expected_sequence)}, but got {len(sequence)}" ) diff --git a/src/backend/tests/unit/graph/test_graph.py b/src/backend/tests/unit/graph/test_graph.py index 2feff41ed..2f34cbd14 100644 --- a/src/backend/tests/unit/graph/test_graph.py +++ b/src/backend/tests/unit/graph/test_graph.py @@ -180,9 +180,9 @@ def test_process_flow_vector_store_grouped(vector_store_grouped_json_flow): for idx, expected_keyword in enumerate(expected_keywords): for key, value in expected_keyword.items(): - assert ( - value in edges[idx][key].split("-")[0] - ), f"Edge {idx}, key {key} expected to contain {value} but got {edges[idx][key]}" + assert value in edges[idx][key].split("-")[0], ( + f"Edge {idx}, key {key} expected to contain {value} but got {edges[idx][key]}" + ) def test_update_template(sample_template, sample_nodes): diff --git a/src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py b/src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py index 516df7fdb..1adcca6d9 100644 --- a/src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py +++ b/src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py @@ -145,7 +145,7 @@ def test_vector_store_rag_dump_components_and_edges(ingestion_graph, rag_graph): for node_id, expected_type in expected_nodes.items(): assert node_id in node_map, f"Missing node {node_id}" assert node_map[node_id]["type"] == expected_type, ( - f"Node {node_id} has incorrect type. " f"Expected {expected_type}, got {node_map[node_id]['type']}" + f"Node {node_id} has incorrect type. Expected {expected_type}, got {node_map[node_id]['type']}" ) # Verify all nodes in graph are expected @@ -226,9 +226,9 @@ def test_vector_store_rag_add(ingestion_graph: Graph, rag_graph: Graph): f"Vertices mismatch: {len(ingestion_graph_copy.vertices)} " f"!= {len(ingestion_graph.vertices)} + {len(rag_graph.vertices)}" ) - assert len(ingestion_graph_copy.edges) == len(ingestion_graph.edges) + len( - rag_graph.edges - ), f"Edges mismatch: {len(ingestion_graph_copy.edges)} != {len(ingestion_graph.edges)} + {len(rag_graph.edges)}" + assert len(ingestion_graph_copy.edges) == len(ingestion_graph.edges) + len(rag_graph.edges), ( + f"Edges mismatch: {len(ingestion_graph_copy.edges)} != {len(ingestion_graph.edges)} + {len(rag_graph.edges)}" + ) combined_graph_dump = ingestion_graph_copy.dump( name="Combined Graph", description="Graph for data ingestion and RAG", endpoint_name="combined" diff --git a/src/backend/tests/unit/test_endpoints.py b/src/backend/tests/unit/test_endpoints.py index ba849c7e7..1c1b7dd3f 100644 --- a/src/backend/tests/unit/test_endpoints.py +++ b/src/backend/tests/unit/test_endpoints.py @@ -417,9 +417,9 @@ async def test_successful_run_with_input_type_text(client, simple_api_test, crea assert len(text_input_outputs) == 1 # Now we check if the input_value is correct # We get text key twice because the output is now a Message - assert all( - output.get("results").get("text").get("text") == "value1" for output in text_input_outputs - ), text_input_outputs + assert all(output.get("results").get("text").get("text") == "value1" for output in text_input_outputs), ( + text_input_outputs + ) @pytest.mark.api_key_required @@ -451,9 +451,9 @@ async def test_successful_run_with_input_type_chat(client: AsyncClient, simple_a chat_input_outputs = [output for output in outputs_dict.get("outputs") if "ChatInput" in output.get("component_id")] assert len(chat_input_outputs) == 1 # Now we check if the input_value is correct - assert all( - output.get("results").get("message").get("text") == "value1" for output in chat_input_outputs - ), chat_input_outputs + assert all(output.get("results").get("message").get("text") == "value1" for output in chat_input_outputs), ( + chat_input_outputs + ) @pytest.mark.benchmark @@ -507,9 +507,9 @@ async def test_successful_run_with_input_type_any(client, simple_api_test, creat all_message_or_text_dicts = [ result_dict.get("message", result_dict.get("text")) for result_dict in all_result_dicts ] - assert all( - message_or_text_dict.get("text") == "value1" for message_or_text_dict in all_message_or_text_dicts - ), any_input_outputs + assert all(message_or_text_dict.get("text") == "value1" for message_or_text_dict in all_message_or_text_dicts), ( + any_input_outputs + ) async def test_invalid_flow_id(client, created_api_key): diff --git a/src/backend/tests/unit/test_initial_setup.py b/src/backend/tests/unit/test_initial_setup.py index 9adb0d18d..9601e8f20 100644 --- a/src/backend/tests/unit/test_initial_setup.py +++ b/src/backend/tests/unit/test_initial_setup.py @@ -15,7 +15,7 @@ from langflow.initial_setup.setup import ( load_starter_projects, update_projects_components_with_latest_component_versions, ) -from langflow.interface.types import aget_all_types_dict +from langflow.interface.components import aget_all_types_dict from langflow.services.database.models import Flow from langflow.services.database.models.folder.model import Folder from langflow.services.deps import get_settings_service, session_scope @@ -52,9 +52,9 @@ async def test_get_project_data(): assert isinstance(updated_at_datetime, datetime), f"Project {project_name} has no updated_at_datetime" assert isinstance(project_data, dict), f"Project {project_name} has no data" assert isinstance(project_icon, str) or project_icon is None, f"Project {project_name} has no icon" - assert ( - isinstance(project_icon_bg_color, str) or project_icon_bg_color is None - ), f"Project {project_name} has no icon_bg_color" + assert isinstance(project_icon_bg_color, str) or project_icon_bg_color is None, ( + f"Project {project_name} has no icon_bg_color" + ) @pytest.mark.usefixtures("client") diff --git a/uv.lock b/uv.lock index c20b32d35..8d4a487c1 100644 --- a/uv.lock +++ b/uv.lock @@ -532,7 +532,7 @@ name = "blessed" version = "1.20.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "jinxed", marker = "platform_system == 'Windows'" }, + { name = "jinxed", marker = "sys_platform == 'win32'" }, { name = "six" }, { name = "wcwidth" }, ] @@ -954,7 +954,7 @@ name = "click" version = "8.1.8" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "platform_system == 'Windows'" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } wheels = [ @@ -3142,7 +3142,7 @@ name = "ipykernel" version = "6.29.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "appnope", marker = "platform_system == 'Darwin'" }, + { name = "appnope", marker = "sys_platform == 'darwin'" }, { name = "comm" }, { name = "debugpy" }, { name = "ipython" }, @@ -3233,7 +3233,7 @@ name = "jinxed" version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ansicon", marker = "platform_system == 'Windows'" }, + { name = "ansicon", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/20/d0/59b2b80e7a52d255f9e0ad040d2e826342d05580c4b1d7d7747cfb8db731/jinxed-1.3.0.tar.gz", hash = "sha256:1593124b18a41b7a3da3b078471442e51dbad3d77b4d4f2b0c26ab6f7d660dbf", size = 80981 } wheels = [ @@ -4201,7 +4201,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.8.4,<0.9.0" }, + { name = "ruff", specifier = ">=0.9.1,<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" }, @@ -6148,7 +6148,7 @@ name = "portalocker" version = "2.10.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pywin32", marker = "platform_system == 'Windows'" }, + { name = "pywin32", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ed/d3/c6c64067759e87af98cc668c1cc75171347d0f1577fab7ca3749134e3cd4/portalocker-2.10.1.tar.gz", hash = "sha256:ef1bf844e878ab08aee7e40184156e1151f228f103aa5c6bd0724cc330960f8f", size = 40891 } wheels = [ @@ -7788,27 +7788,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.8.4" +version = "0.9.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/34/37/9c02181ef38d55b77d97c68b78e705fd14c0de0e5d085202bb2b52ce5be9/ruff-0.8.4.tar.gz", hash = "sha256:0d5f89f254836799af1615798caa5f80b7f935d7a670fad66c5007928e57ace8", size = 3402103 } +sdist = { url = "https://files.pythonhosted.org/packages/67/3e/e89f736f01aa9517a97e2e7e0ce8d34a4d8207087b3cfdec95133fee13b5/ruff-0.9.1.tar.gz", hash = "sha256:fd2b25ecaf907d6458fa842675382c8597b3c746a2dde6717fe3415425df0c17", size = 3498844 } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/67/f480bf2f2723b2e49af38ed2be75ccdb2798fca7d56279b585c8f553aaab/ruff-0.8.4-py3-none-linux_armv6l.whl", hash = "sha256:58072f0c06080276804c6a4e21a9045a706584a958e644353603d36ca1eb8a60", size = 10546415 }, - { url = "https://files.pythonhosted.org/packages/eb/7a/5aba20312c73f1ce61814e520d1920edf68ca3b9c507bd84d8546a8ecaa8/ruff-0.8.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ffb60904651c00a1e0b8df594591770018a0f04587f7deeb3838344fe3adabac", size = 10346113 }, - { url = "https://files.pythonhosted.org/packages/76/f4/c41de22b3728486f0aa95383a44c42657b2db4062f3234ca36fc8cf52d8b/ruff-0.8.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ddf5d654ac0d44389f6bf05cee4caeefc3132a64b58ea46738111d687352296", size = 9943564 }, - { url = "https://files.pythonhosted.org/packages/0e/f0/afa0d2191af495ac82d4cbbfd7a94e3df6f62a04ca412033e073b871fc6d/ruff-0.8.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e248b1f0fa2749edd3350a2a342b67b43a2627434c059a063418e3d375cfe643", size = 10805522 }, - { url = "https://files.pythonhosted.org/packages/12/57/5d1e9a0fd0c228e663894e8e3a8e7063e5ee90f8e8e60cf2085f362bfa1a/ruff-0.8.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf197b98ed86e417412ee3b6c893f44c8864f816451441483253d5ff22c0e81e", size = 10306763 }, - { url = "https://files.pythonhosted.org/packages/04/df/f069fdb02e408be8aac6853583572a2873f87f866fe8515de65873caf6b8/ruff-0.8.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c41319b85faa3aadd4d30cb1cffdd9ac6b89704ff79f7664b853785b48eccdf3", size = 11359574 }, - { url = "https://files.pythonhosted.org/packages/d3/04/37c27494cd02e4a8315680debfc6dfabcb97e597c07cce0044db1f9dfbe2/ruff-0.8.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9f8402b7c4f96463f135e936d9ab77b65711fcd5d72e5d67597b543bbb43cf3f", size = 12094851 }, - { url = "https://files.pythonhosted.org/packages/81/b1/c5d7fb68506cab9832d208d03ea4668da9a9887a4a392f4f328b1bf734ad/ruff-0.8.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4e56b3baa9c23d324ead112a4fdf20db9a3f8f29eeabff1355114dd96014604", size = 11655539 }, - { url = "https://files.pythonhosted.org/packages/ef/38/8f8f2c8898dc8a7a49bc340cf6f00226917f0f5cb489e37075bcb2ce3671/ruff-0.8.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:736272574e97157f7edbbb43b1d046125fce9e7d8d583d5d65d0c9bf2c15addf", size = 12912805 }, - { url = "https://files.pythonhosted.org/packages/06/dd/fa6660c279f4eb320788876d0cff4ea18d9af7d9ed7216d7bd66877468d0/ruff-0.8.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fe710ab6061592521f902fca7ebcb9fabd27bc7c57c764298b1c1f15fff720", size = 11205976 }, - { url = "https://files.pythonhosted.org/packages/a8/d7/de94cc89833b5de455750686c17c9e10f4e1ab7ccdc5521b8fe911d1477e/ruff-0.8.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:13e9ec6d6b55f6da412d59953d65d66e760d583dd3c1c72bf1f26435b5bfdbae", size = 10792039 }, - { url = "https://files.pythonhosted.org/packages/6d/15/3e4906559248bdbb74854af684314608297a05b996062c9d72e0ef7c7097/ruff-0.8.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:97d9aefef725348ad77d6db98b726cfdb075a40b936c7984088804dfd38268a7", size = 10400088 }, - { url = "https://files.pythonhosted.org/packages/a2/21/9ed4c0e8133cb4a87a18d470f534ad1a8a66d7bec493bcb8bda2d1a5d5be/ruff-0.8.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ab78e33325a6f5374e04c2ab924a3367d69a0da36f8c9cb6b894a62017506111", size = 10900814 }, - { url = "https://files.pythonhosted.org/packages/0d/5d/122a65a18955bd9da2616b69bc839351f8baf23b2805b543aa2f0aed72b5/ruff-0.8.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8ef06f66f4a05c3ddbc9121a8b0cecccd92c5bf3dd43b5472ffe40b8ca10f0f8", size = 11268828 }, - { url = "https://files.pythonhosted.org/packages/43/a9/1676ee9106995381e3d34bccac5bb28df70194167337ed4854c20f27c7ba/ruff-0.8.4-py3-none-win32.whl", hash = "sha256:552fb6d861320958ca5e15f28b20a3d071aa83b93caee33a87b471f99a6c0835", size = 8805621 }, - { url = "https://files.pythonhosted.org/packages/10/98/ed6b56a30ee76771c193ff7ceeaf1d2acc98d33a1a27b8479cbdb5c17a23/ruff-0.8.4-py3-none-win_amd64.whl", hash = "sha256:f21a1143776f8656d7f364bd264a9d60f01b7f52243fbe90e7670c0dfe0cf65d", size = 9660086 }, - { url = "https://files.pythonhosted.org/packages/13/9f/026e18ca7d7766783d779dae5e9c656746c6ede36ef73c6d934aaf4a6dec/ruff-0.8.4-py3-none-win_arm64.whl", hash = "sha256:9183dd615d8df50defa8b1d9a074053891ba39025cf5ae88e8bcb52edcc4bf08", size = 9074500 }, + { url = "https://files.pythonhosted.org/packages/dc/05/c3a2e0feb3d5d394cdfd552de01df9d3ec8a3a3771bbff247fab7e668653/ruff-0.9.1-py3-none-linux_armv6l.whl", hash = "sha256:84330dda7abcc270e6055551aca93fdde1b0685fc4fd358f26410f9349cf1743", size = 10645241 }, + { url = "https://files.pythonhosted.org/packages/dd/da/59f0a40e5f88ee5c054ad175caaa2319fc96571e1d29ab4730728f2aad4f/ruff-0.9.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3cae39ba5d137054b0e5b472aee3b78a7c884e61591b100aeb544bcd1fc38d4f", size = 10391066 }, + { url = "https://files.pythonhosted.org/packages/b7/fe/85e1c1acf0ba04a3f2d54ae61073da030f7a5dc386194f96f3c6ca444a78/ruff-0.9.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:50c647ff96f4ba288db0ad87048257753733763b409b2faf2ea78b45c8bb7fcb", size = 10012308 }, + { url = "https://files.pythonhosted.org/packages/6f/9b/780aa5d4bdca8dcea4309264b8faa304bac30e1ce0bcc910422bfcadd203/ruff-0.9.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0c8b149e9c7353cace7d698e1656ffcf1e36e50f8ea3b5d5f7f87ff9986a7ca", size = 10881960 }, + { url = "https://files.pythonhosted.org/packages/12/f4/dac4361afbfe520afa7186439e8094e4884ae3b15c8fc75fb2e759c1f267/ruff-0.9.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:beb3298604540c884d8b282fe7625651378e1986c25df51dec5b2f60cafc31ce", size = 10414803 }, + { url = "https://files.pythonhosted.org/packages/f0/a2/057a3cb7999513cb78d6cb33a7d1cc6401c82d7332583786e4dad9e38e44/ruff-0.9.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39d0174ccc45c439093971cc06ed3ac4dc545f5e8bdacf9f067adf879544d969", size = 11464929 }, + { url = "https://files.pythonhosted.org/packages/eb/c6/1ccfcc209bee465ced4874dcfeaadc88aafcc1ea9c9f31ef66f063c187f0/ruff-0.9.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:69572926c0f0c9912288915214ca9b2809525ea263603370b9e00bed2ba56dbd", size = 12170717 }, + { url = "https://files.pythonhosted.org/packages/84/97/4a524027518525c7cf6931e9fd3b2382be5e4b75b2b61bec02681a7685a5/ruff-0.9.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:937267afce0c9170d6d29f01fcd1f4378172dec6760a9f4dface48cdabf9610a", size = 11708921 }, + { url = "https://files.pythonhosted.org/packages/a6/a4/4e77cf6065c700d5593b25fca6cf725b1ab6d70674904f876254d0112ed0/ruff-0.9.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:186c2313de946f2c22bdf5954b8dd083e124bcfb685732cfb0beae0c47233d9b", size = 13058074 }, + { url = "https://files.pythonhosted.org/packages/f9/d6/fcb78e0531e863d0a952c4c5600cc5cd317437f0e5f031cd2288b117bb37/ruff-0.9.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f94942a3bb767675d9a051867c036655fe9f6c8a491539156a6f7e6b5f31831", size = 11281093 }, + { url = "https://files.pythonhosted.org/packages/e4/3b/7235bbeff00c95dc2d073cfdbf2b871b5bbf476754c5d277815d286b4328/ruff-0.9.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:728d791b769cc28c05f12c280f99e8896932e9833fef1dd8756a6af2261fd1ab", size = 10882610 }, + { url = "https://files.pythonhosted.org/packages/2a/66/5599d23257c61cf038137f82999ca8f9d0080d9d5134440a461bef85b461/ruff-0.9.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2f312c86fb40c5c02b44a29a750ee3b21002bd813b5233facdaf63a51d9a85e1", size = 10489273 }, + { url = "https://files.pythonhosted.org/packages/78/85/de4aa057e2532db0f9761e2c2c13834991e087787b93e4aeb5f1cb10d2df/ruff-0.9.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ae017c3a29bee341ba584f3823f805abbe5fe9cd97f87ed07ecbf533c4c88366", size = 11003314 }, + { url = "https://files.pythonhosted.org/packages/00/42/afedcaa089116d81447347f76041ff46025849fedb0ed2b187d24cf70fca/ruff-0.9.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5dc40a378a0e21b4cfe2b8a0f1812a6572fc7b230ef12cd9fac9161aa91d807f", size = 11342982 }, + { url = "https://files.pythonhosted.org/packages/39/c6/fe45f3eb27e3948b41a305d8b768e949bf6a39310e9df73f6c576d7f1d9f/ruff-0.9.1-py3-none-win32.whl", hash = "sha256:46ebf5cc106cf7e7378ca3c28ce4293b61b449cd121b98699be727d40b79ba72", size = 8819750 }, + { url = "https://files.pythonhosted.org/packages/38/8d/580db77c3b9d5c3d9479e55b0b832d279c30c8f00ab0190d4cd8fc67831c/ruff-0.9.1-py3-none-win_amd64.whl", hash = "sha256:342a824b46ddbcdddd3abfbb332fa7fcaac5488bf18073e841236aadf4ad5c19", size = 9701331 }, + { url = "https://files.pythonhosted.org/packages/b2/94/0498cdb7316ed67a1928300dd87d659c933479f44dec51b4f62bfd1f8028/ruff-0.9.1-py3-none-win_arm64.whl", hash = "sha256:1cd76c7f9c679e6e8f2af8f778367dca82b95009bc7b1a85a47f1521ae524fa7", size = 9145708 }, ] [[package]] @@ -8645,19 +8645,19 @@ dependencies = [ { name = "fsspec" }, { name = "jinja2" }, { name = "networkx" }, - { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, - { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "sympy" }, - { name = "triton", marker = "python_full_version < '3.13' and platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "triton", marker = "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "typing-extensions" }, ] wheels = [ @@ -8698,7 +8698,7 @@ name = "tqdm" version = "4.67.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "platform_system == 'Windows'" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } wheels = [