diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1e04f6a..2a8726d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,6 +7,24 @@ on: branches: [main] jobs: + lint: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v5 + + - uses: actions/setup-python@v6 + with: + python-version: "3.11" + + - name: Install lint dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -e ".[dev]" + + - name: Run Ruff + run: ruff check wavemind tests benchmarks examples + pytest: runs-on: ubuntu-latest strategy: diff --git a/benchmarks/agent_memory_benchmark.py b/benchmarks/agent_memory_benchmark.py index 45c80c2..cb4ea9d 100644 --- a/benchmarks/agent_memory_benchmark.py +++ b/benchmarks/agent_memory_benchmark.py @@ -15,7 +15,7 @@ sys.path.insert(0, str(PROJECT_ROOT)) from wavemind import WaveMind -from wavemind.encoders import HashingTextEncoder, create_text_encoder +from wavemind.encoders import create_text_encoder @dataclass(frozen=True) diff --git a/benchmarks/benchmark_registry.py b/benchmarks/benchmark_registry.py index 6670b5d..fb6bc20 100644 --- a/benchmarks/benchmark_registry.py +++ b/benchmarks/benchmark_registry.py @@ -653,8 +653,6 @@ def _implemented_entries(root: Path) -> list[dict[str, Any]]: postgres_pitr_payload = _load_json(root / "benchmarks" / "postgres_pitr_plan.json") memory_competitor_payload = _load_json(root / "benchmarks" / "memory_competitor_results.json") answer_payload = _load_json(root / "benchmarks" / "longmemeval_answer_extractive_20_results.json") - vectordbbench_payload = _load_json(root / "benchmarks" / "vectordbbench_dataset_manifest.json") - agent_results = _engine_results(agent_payload) agent_coherence_results = _engine_results(agent_coherence_payload) dynamic_results = _engine_results(dynamic_payload) @@ -722,19 +720,6 @@ def _implemented_entries(root: Path) -> list[dict[str, Any]]: ) postgres_pitr_validation = postgres_pitr_profile.get("validation", {}) memory_competitor_results = _engine_results(memory_competitor_payload) - vectordbbench_summary = ( - { - "WaveMind custom dataset export": { - "status": vectordbbench_payload.get("status"), - "vectors": vectordbbench_payload.get("dataset", {}).get("vectors"), - "queries": vectordbbench_payload.get("dataset", {}).get("queries"), - "dim": vectordbbench_payload.get("dataset", {}).get("dim"), - "top_k": vectordbbench_payload.get("dataset", {}).get("top_k"), - } - } - if vectordbbench_payload - else {} - ) answer_qwen05_payload = _load_json(root / "benchmarks" / "longmemeval_answer_qwen25_0_5b_50_results.json") answer_qwen15_payload = _load_json(root / "benchmarks" / "longmemeval_answer_qwen25_1_5b_50_results.json") answer_results = { diff --git a/benchmarks/kubernetes_serverless_lifecycle_smoke.py b/benchmarks/kubernetes_serverless_lifecycle_smoke.py index 478dd09..843de85 100644 --- a/benchmarks/kubernetes_serverless_lifecycle_smoke.py +++ b/benchmarks/kubernetes_serverless_lifecycle_smoke.py @@ -5,7 +5,6 @@ import json import math import os -import statistics import subprocess import tempfile import time diff --git a/benchmarks/memory_competitor_benchmark.py b/benchmarks/memory_competitor_benchmark.py index 5ddecee..d235213 100644 --- a/benchmarks/memory_competitor_benchmark.py +++ b/benchmarks/memory_competitor_benchmark.py @@ -12,7 +12,7 @@ import time import uuid from collections import defaultdict -from dataclasses import asdict, dataclass +from dataclasses import dataclass from datetime import datetime, timedelta, timezone from pathlib import Path from typing import Any, Callable, Iterable @@ -446,7 +446,10 @@ def run_zep( from zep_cloud import Message except Exception as exc: return skipped_result("Zep", f"Import zep-cloud failed: {exc}") - client_factory = lambda: Zep(base_url=api_url, api_key=api_key, timeout=timeout) + def create_zep_cloud_client() -> Any: + return Zep(base_url=api_url, api_key=api_key, timeout=timeout) + + client_factory = create_zep_cloud_client message_factory = Message elif _module_available("zep_python"): try: @@ -454,7 +457,10 @@ def run_zep( from zep_python.types.message import Message except Exception as exc: return skipped_result("Zep", f"Import zep-python failed: {exc}") - client_factory = lambda: Zep(base_url=api_url, api_key=api_key, timeout=timeout) + def create_zep_python_client() -> Any: + return Zep(base_url=api_url, api_key=api_key, timeout=timeout) + + client_factory = create_zep_python_client message_factory = Message else: return skipped_result( @@ -462,7 +468,10 @@ def run_zep( 'Install a Zep SDK to run this adapter profile: pip install "zep-cloud" or pip install "zep-python"', ) if message_factory is None: - message_factory = lambda **kwargs: kwargs + def create_message(**kwargs: Any) -> dict[str, Any]: + return kwargs + + message_factory = create_message client = client_factory() if hasattr(client, "memory"): diff --git a/benchmarks/scale_readiness_benchmark.py b/benchmarks/scale_readiness_benchmark.py index 02d008e..e9729a3 100644 --- a/benchmarks/scale_readiness_benchmark.py +++ b/benchmarks/scale_readiness_benchmark.py @@ -67,7 +67,6 @@ operator_status, query_with_cache, query_with_vector_cache, - remember_payload, run_control_plane_consensus_profile, serverless_sample_bundle, sync_namespace_delta, @@ -1798,8 +1797,6 @@ def run_api_cache_mutation_profile() -> dict[str, object]: second_query.raise_for_status() latencies.append((time.perf_counter() - started) * 1000.0) second_results = second_query.json()["results"] - cache_keys_after_second_query = len(client.items) - feedback_response = api.post( "/feedback", json={ diff --git a/examples/agent_with_memory.py b/examples/agent_with_memory.py index f9ed723..885ccd0 100644 --- a/examples/agent_with_memory.py +++ b/examples/agent_with_memory.py @@ -3,7 +3,6 @@ import argparse import json import os -import re import sys import urllib.request from pathlib import Path diff --git a/pyproject.toml b/pyproject.toml index e3327eb..44dbeee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -72,7 +72,9 @@ production = [ dev = [ "pytest>=8", "httpx>=0.27", + "httpx2>=2", "langchain-classic>=1.0", + "ruff>=0.12", ] [project.scripts] @@ -85,3 +87,10 @@ Repository = "https://github.com/CaspianG/wavemind" [tool.setuptools.packages.find] where = ["."] include = ["wavemind*"] + +[tool.ruff] +target-version = "py310" + +[tool.ruff.lint.per-file-ignores] +"benchmarks/*.py" = ["E402"] +"examples/*.py" = ["E402"] diff --git a/wavemind/__init__.py b/wavemind/__init__.py index 318e326..7fdc1ef 100644 --- a/wavemind/__init__.py +++ b/wavemind/__init__.py @@ -431,7 +431,6 @@ "render_active_active_admission_markdown", "render_scale_gap_markdown", "render_serverless_admission_markdown", - "render_active_active_admission_markdown", "render_production_admission_markdown", "render_memory_os_canary_markdown", "render_memory_os_policy_evolution_markdown", @@ -454,6 +453,7 @@ "validate_precomputed_cross_modal_contract", "validate_external_multimodal_evidence", "validate_postgres_pitr_commands", + "video_payload", "write_memory_os_policy_evolution_artifacts", "write_production_evidence_env_artifacts", ] diff --git a/wavemind/cli.py b/wavemind/cli.py index 4c9b955..263c312 100644 --- a/wavemind/cli.py +++ b/wavemind/cli.py @@ -60,7 +60,6 @@ run_memory_os_canary, ) from .memory_os_evolution import ( - render_memory_os_policy_evolution_markdown, run_memory_os_policy_evolution, write_memory_os_policy_evolution_artifacts, ) @@ -102,7 +101,6 @@ ) from .production_evidence_env import ( build_production_evidence_env_contract, - render_production_evidence_env_markdown, write_production_evidence_env_artifacts, ) from .production_evidence_ingest import ( diff --git a/wavemind/cloud_run_evidence.py b/wavemind/cloud_run_evidence.py index a1c6423..f73b830 100644 --- a/wavemind/cloud_run_evidence.py +++ b/wavemind/cloud_run_evidence.py @@ -75,11 +75,11 @@ def _annotation(service: Mapping[str, Any], *names: str) -> str | None: service.get("spec", {}).get("template", {}).get("metadata", {}).get("annotations", {}), service.get("template", {}).get("annotations", {}), ] - for annotations in candidates: - if not isinstance(annotations, Mapping): + for metadata_annotations in candidates: + if not isinstance(metadata_annotations, Mapping): continue for name in names: - value = annotations.get(name) + value = metadata_annotations.get(name) if value is not None and str(value).strip(): return str(value).strip() return None diff --git a/wavemind/cluster.py b/wavemind/cluster.py index 6b5dcbd..51283d4 100644 --- a/wavemind/cluster.py +++ b/wavemind/cluster.py @@ -144,7 +144,6 @@ def quorum_report(self) -> dict[str, object]: def placement_health_report(self) -> dict[str, object]: namespace_count = len(self.placements) - zone_by_node = {node.id: node.zone for node in self.nodes} failure_domain_by_node = { node.id: node.zone or node.id for node in self.nodes diff --git a/wavemind/production_evidence.py b/wavemind/production_evidence.py index 6ec4ccc..a26a906 100644 --- a/wavemind/production_evidence.py +++ b/wavemind/production_evidence.py @@ -1925,11 +1925,6 @@ def build_production_evidence_dispatch_plan( root = Path(root) strict = evaluate_production_evidence(root) preflight = evaluate_production_evidence_preflight(root, env=env) - strict_by_id = { - str(row.get("id")): row - for row in strict.get("requirements", []) - if isinstance(row, dict) - } preflight_by_id = { str(row.get("id")): row for row in preflight.get("checks", []) diff --git a/wavemind/remote_lab.py b/wavemind/remote_lab.py index 2dc71b6..6909222 100644 --- a/wavemind/remote_lab.py +++ b/wavemind/remote_lab.py @@ -3,7 +3,6 @@ import hashlib import ipaddress import json -import os import re import socket import subprocess @@ -12,7 +11,7 @@ import urllib.request from dataclasses import dataclass from pathlib import Path -from typing import Any, Callable, Mapping, Sequence +from typing import Any, Callable, Mapping from urllib.parse import quote, urlparse from .active_active_drill import run_active_active_drill