From 829e82a9afa404750494b658bb9c208386aec9e4 Mon Sep 17 00:00:00 2001 From: Willem Jiang Date: Sun, 26 Apr 2026 15:09:25 +0800 Subject: [PATCH] fix the lint error in backend --- backend/app/gateway/routers/threads.py | 1 - backend/app/gateway/services.py | 4 +- .../deerflow/persistence/feedback/model.py | 4 +- backend/scripts/migrate_user_isolation.py | 3 +- backend/tests/_router_auth_helpers.py | 7 +-- .../tests/test_memory_queue_user_isolation.py | 1 + .../test_memory_storage_user_isolation.py | 8 ++-- .../test_memory_updater_user_isolation.py | 3 +- .../tests/test_migration_user_isolation.py | 13 +++++- backend/tests/test_paths_user_isolation.py | 4 +- .../tests/test_run_event_store_pagination.py | 46 +++++++++++++------ backend/tests/test_run_journal.py | 2 - backend/tests/test_runs_api_endpoints.py | 14 +++--- .../test_thread_run_messages_pagination.py | 12 +++-- backend/tests/test_user_context.py | 3 +- 15 files changed, 76 insertions(+), 49 deletions(-) diff --git a/backend/app/gateway/routers/threads.py b/backend/app/gateway/routers/threads.py index c7bfa69b6..484582839 100644 --- a/backend/app/gateway/routers/threads.py +++ b/backend/app/gateway/routers/threads.py @@ -13,7 +13,6 @@ matching the LangGraph Platform wire format expected by the from __future__ import annotations import logging -import re import time import uuid from typing import Any diff --git a/backend/app/gateway/services.py b/backend/app/gateway/services.py index 7fc22d411..c6704c02d 100644 --- a/backend/app/gateway/services.py +++ b/backend/app/gateway/services.py @@ -8,18 +8,16 @@ frames, and consuming stream bridge events. Router modules from __future__ import annotations import asyncio -import dataclasses import json import logging import re -import time from collections.abc import Mapping from typing import Any from fastapi import HTTPException, Request from langchain_core.messages import HumanMessage -from app.gateway.deps import get_run_context, get_run_manager, get_run_store, get_stream_bridge +from app.gateway.deps import get_run_context, get_run_manager, get_stream_bridge from app.gateway.utils import sanitize_log_param from deerflow.runtime import ( END_SENTINEL, diff --git a/backend/packages/harness/deerflow/persistence/feedback/model.py b/backend/packages/harness/deerflow/persistence/feedback/model.py index f06bc84e7..a9b6479b3 100644 --- a/backend/packages/harness/deerflow/persistence/feedback/model.py +++ b/backend/packages/harness/deerflow/persistence/feedback/model.py @@ -13,9 +13,7 @@ from deerflow.persistence.base import Base class FeedbackRow(Base): __tablename__ = "feedback" - __table_args__ = ( - UniqueConstraint("thread_id", "run_id", "user_id", name="uq_feedback_thread_run_user"), - ) + __table_args__ = (UniqueConstraint("thread_id", "run_id", "user_id", name="uq_feedback_thread_run_user"),) feedback_id: Mapped[str] = mapped_column(String(64), primary_key=True) run_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True) diff --git a/backend/scripts/migrate_user_isolation.py b/backend/scripts/migrate_user_isolation.py index 4d37a0d1e..82923e4b7 100644 --- a/backend/scripts/migrate_user_isolation.py +++ b/backend/scripts/migrate_user_isolation.py @@ -5,11 +5,10 @@ Usage: The script is idempotent — re-running it after a successful migration is a no-op. """ + import argparse -import json import logging import shutil -from pathlib import Path from deerflow.config.paths import Paths, get_paths diff --git a/backend/tests/_router_auth_helpers.py b/backend/tests/_router_auth_helpers.py index a7ce60468..2bd2ebdee 100644 --- a/backend/tests/_router_auth_helpers.py +++ b/backend/tests/_router_auth_helpers.py @@ -29,7 +29,6 @@ apps with the real middleware — those should not use this module. from __future__ import annotations from collections.abc import Callable -from typing import ParamSpec, TypeVar from unittest.mock import AsyncMock, MagicMock from uuid import uuid4 @@ -113,11 +112,7 @@ def make_authed_test_app( return app -_P = ParamSpec("_P") -_R = TypeVar("_R") - - -def call_unwrapped(decorated: Callable[_P, _R], /, *args: _P.args, **kwargs: _P.kwargs) -> _R: +def call_unwrapped[*P, R](decorated: Callable[P, R], /, *args: P.args, **kwargs: P.kwargs) -> R: """Invoke the underlying function of a ``@require_permission``-decorated route. ``functools.wraps`` sets ``__wrapped__`` on each layer; we walk all diff --git a/backend/tests/test_memory_queue_user_isolation.py b/backend/tests/test_memory_queue_user_isolation.py index 1a209d659..cf068e095 100644 --- a/backend/tests/test_memory_queue_user_isolation.py +++ b/backend/tests/test_memory_queue_user_isolation.py @@ -1,4 +1,5 @@ """Tests for user_id propagation through memory queue.""" + from unittest.mock import MagicMock, patch from deerflow.agents.memory.queue import ConversationContext, MemoryUpdateQueue diff --git a/backend/tests/test_memory_storage_user_isolation.py b/backend/tests/test_memory_storage_user_isolation.py index a82fffa50..5dd114b7e 100644 --- a/backend/tests/test_memory_storage_user_isolation.py +++ b/backend/tests/test_memory_storage_user_isolation.py @@ -1,8 +1,10 @@ """Tests for per-user memory storage isolation.""" -import pytest + from pathlib import Path from unittest.mock import patch +import pytest + from deerflow.agents.memory.storage import FileMemoryStorage, create_empty_memory @@ -65,8 +67,8 @@ class TestUserIsolatedStorage: assert loaded_a["user"]["workContext"]["summary"] == "A" def test_no_user_id_uses_legacy_path(self, base_dir: Path): - from deerflow.config.paths import Paths from deerflow.config.memory_config import MemoryConfig + from deerflow.config.paths import Paths paths = Paths(base_dir) with patch("deerflow.agents.memory.storage.get_paths", return_value=paths): @@ -79,8 +81,8 @@ class TestUserIsolatedStorage: def test_user_and_legacy_do_not_interfere(self, base_dir: Path): """user_id=None (legacy) and user_id='alice' must use different files and caches.""" - from deerflow.config.paths import Paths from deerflow.config.memory_config import MemoryConfig + from deerflow.config.paths import Paths paths = Paths(base_dir) with patch("deerflow.agents.memory.storage.get_paths", return_value=paths): diff --git a/backend/tests/test_memory_updater_user_isolation.py b/backend/tests/test_memory_updater_user_isolation.py index d38f3fc90..da8a444fe 100644 --- a/backend/tests/test_memory_updater_user_isolation.py +++ b/backend/tests/test_memory_updater_user_isolation.py @@ -1,7 +1,8 @@ """Tests for user_id propagation in memory updater.""" + from unittest.mock import MagicMock, patch -from deerflow.agents.memory.updater import get_memory_data, clear_memory_data, _save_memory_to_file +from deerflow.agents.memory.updater import _save_memory_to_file, clear_memory_data, get_memory_data def test_get_memory_data_passes_user_id(): diff --git a/backend/tests/test_migration_user_isolation.py b/backend/tests/test_migration_user_isolation.py index 8a07c2130..dbb20bdd8 100644 --- a/backend/tests/test_migration_user_isolation.py +++ b/backend/tests/test_migration_user_isolation.py @@ -1,8 +1,10 @@ """Tests for per-user data migration.""" + import json -import pytest from pathlib import Path +import pytest + from deerflow.config.paths import Paths @@ -23,6 +25,7 @@ class TestMigrateThreadDirs: (legacy / "file.txt").write_text("hello") from scripts.migrate_user_isolation import migrate_thread_dirs + migrate_thread_dirs(paths, thread_owner_map={"t1": "alice"}) expected = base_dir / "users" / "alice" / "threads" / "t1" / "user-data" / "workspace" / "file.txt" @@ -35,6 +38,7 @@ class TestMigrateThreadDirs: legacy.mkdir(parents=True) from scripts.migrate_user_isolation import migrate_thread_dirs + migrate_thread_dirs(paths, thread_owner_map={}) expected = base_dir / "users" / "default" / "threads" / "t2" @@ -45,6 +49,7 @@ class TestMigrateThreadDirs: new_dir.mkdir(parents=True) from scripts.migrate_user_isolation import migrate_thread_dirs + migrate_thread_dirs(paths, thread_owner_map={"t1": "alice"}) assert new_dir.exists() @@ -58,6 +63,7 @@ class TestMigrateThreadDirs: (dest / "new.txt").write_text("new") from scripts.migrate_user_isolation import migrate_thread_dirs + migrate_thread_dirs(paths, thread_owner_map={"t1": "alice"}) assert (dest / "new.txt").read_text() == "new" @@ -69,6 +75,7 @@ class TestMigrateThreadDirs: legacy.mkdir(parents=True) from scripts.migrate_user_isolation import migrate_thread_dirs + migrate_thread_dirs(paths, thread_owner_map={}) assert not (base_dir / "threads").exists() @@ -78,6 +85,7 @@ class TestMigrateThreadDirs: legacy.mkdir(parents=True) from scripts.migrate_user_isolation import migrate_thread_dirs + report = migrate_thread_dirs(paths, thread_owner_map={"t1": "alice"}, dry_run=True) assert len(report) == 1 @@ -91,6 +99,7 @@ class TestMigrateMemory: legacy_mem.write_text(json.dumps({"version": "1.0", "facts": []})) from scripts.migrate_user_isolation import migrate_memory + migrate_memory(paths, user_id="default") expected = base_dir / "users" / "default" / "memory.json" @@ -106,6 +115,7 @@ class TestMigrateMemory: dest.write_text(json.dumps({"version": "new"})) from scripts.migrate_user_isolation import migrate_memory + migrate_memory(paths, user_id="default") assert json.loads(dest.read_text())["version"] == "new" @@ -113,4 +123,5 @@ class TestMigrateMemory: def test_no_legacy_memory_is_noop(self, base_dir: Path, paths: Paths): from scripts.migrate_user_isolation import migrate_memory + migrate_memory(paths, user_id="default") # should not raise diff --git a/backend/tests/test_paths_user_isolation.py b/backend/tests/test_paths_user_isolation.py index e74276a32..8f312dcff 100644 --- a/backend/tests/test_paths_user_isolation.py +++ b/backend/tests/test_paths_user_isolation.py @@ -1,7 +1,9 @@ """Tests for user-scoped path resolution in Paths.""" -import pytest + from pathlib import Path +import pytest + from deerflow.config.paths import Paths diff --git a/backend/tests/test_run_event_store_pagination.py b/backend/tests/test_run_event_store_pagination.py index ac5ba4c2d..14a09610c 100644 --- a/backend/tests/test_run_event_store_pagination.py +++ b/backend/tests/test_run_event_store_pagination.py @@ -1,4 +1,5 @@ """Tests for paginated list_messages_by_run across all RunEventStore backends.""" + import pytest from deerflow.runtime.events.store.memory import MemoryRunEventStore @@ -14,14 +15,19 @@ async def test_list_messages_by_run_default_returns_all(base_store): store = base_store for i in range(7): await store.put( - thread_id="t1", run_id="run-a", + thread_id="t1", + run_id="run-a", event_type="human_message" if i % 2 == 0 else "ai_message", - category="message", content=f"msg-a-{i}", + category="message", + content=f"msg-a-{i}", ) for i in range(3): await store.put( - thread_id="t1", run_id="run-b", - event_type="human_message", category="message", content=f"msg-b-{i}", + thread_id="t1", + run_id="run-b", + event_type="human_message", + category="message", + content=f"msg-b-{i}", ) await store.put(thread_id="t1", run_id="run-a", event_type="tool_call", category="trace", content="trace") @@ -36,9 +42,11 @@ async def test_list_messages_by_run_with_limit(base_store): store = base_store for i in range(7): await store.put( - thread_id="t1", run_id="run-a", + thread_id="t1", + run_id="run-a", event_type="human_message" if i % 2 == 0 else "ai_message", - category="message", content=f"msg-a-{i}", + category="message", + content=f"msg-a-{i}", ) msgs = await store.list_messages_by_run("t1", "run-a", limit=3) @@ -52,9 +60,11 @@ async def test_list_messages_by_run_after_seq(base_store): store = base_store for i in range(7): await store.put( - thread_id="t1", run_id="run-a", + thread_id="t1", + run_id="run-a", event_type="human_message" if i % 2 == 0 else "ai_message", - category="message", content=f"msg-a-{i}", + category="message", + content=f"msg-a-{i}", ) all_msgs = await store.list_messages_by_run("t1", "run-a") @@ -69,9 +79,11 @@ async def test_list_messages_by_run_before_seq(base_store): store = base_store for i in range(7): await store.put( - thread_id="t1", run_id="run-a", + thread_id="t1", + run_id="run-a", event_type="human_message" if i % 2 == 0 else "ai_message", - category="message", content=f"msg-a-{i}", + category="message", + content=f"msg-a-{i}", ) all_msgs = await store.list_messages_by_run("t1", "run-a") @@ -86,13 +98,19 @@ async def test_list_messages_by_run_does_not_include_other_run(base_store): store = base_store for i in range(7): await store.put( - thread_id="t1", run_id="run-a", - event_type="human_message", category="message", content=f"msg-a-{i}", + thread_id="t1", + run_id="run-a", + event_type="human_message", + category="message", + content=f"msg-a-{i}", ) for i in range(3): await store.put( - thread_id="t1", run_id="run-b", - event_type="human_message", category="message", content=f"msg-b-{i}", + thread_id="t1", + run_id="run-b", + event_type="human_message", + category="message", + content=f"msg-b-{i}", ) msgs = await store.list_messages_by_run("t1", "run-b") diff --git a/backend/tests/test_run_journal.py b/backend/tests/test_run_journal.py index 0a274df33..a70d02b9b 100644 --- a/backend/tests/test_run_journal.py +++ b/backend/tests/test_run_journal.py @@ -381,5 +381,3 @@ class TestMiddlewareEvents: event_types = {e["event_type"] for e in events} assert "middleware:title" in event_types assert "middleware:guardrail" in event_types - - diff --git a/backend/tests/test_runs_api_endpoints.py b/backend/tests/test_runs_api_endpoints.py index e6b73d865..1826e4d8e 100644 --- a/backend/tests/test_runs_api_endpoints.py +++ b/backend/tests/test_runs_api_endpoints.py @@ -1,15 +1,14 @@ """Tests for GET /api/runs/{run_id}/messages and GET /api/runs/{run_id}/feedback endpoints.""" + from __future__ import annotations -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import AsyncMock, MagicMock -import pytest from _router_auth_helpers import make_authed_test_app from fastapi.testclient import TestClient from app.gateway.routers import runs - # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- @@ -113,7 +112,8 @@ def test_run_messages_passes_after_seq_to_event_store(): response = client.get("/api/runs/run-3/messages?after_seq=5") assert response.status_code == 200 event_store.list_messages_by_run.assert_awaited_once_with( - "thread-3", "run-3", + "thread-3", + "run-3", limit=51, # default limit(50) + 1 before_seq=None, after_seq=5, @@ -133,7 +133,8 @@ def test_run_messages_respects_custom_limit(): response = client.get("/api/runs/run-4/messages?limit=10") assert response.status_code == 200 event_store.list_messages_by_run.assert_awaited_once_with( - "thread-4", "run-4", + "thread-4", + "run-4", limit=11, # 10 + 1 before_seq=None, after_seq=None, @@ -153,7 +154,8 @@ def test_run_messages_passes_before_seq_to_event_store(): response = client.get("/api/runs/run-5/messages?before_seq=10") assert response.status_code == 200 event_store.list_messages_by_run.assert_awaited_once_with( - "thread-5", "run-5", + "thread-5", + "run-5", limit=51, before_seq=10, after_seq=None, diff --git a/backend/tests/test_thread_run_messages_pagination.py b/backend/tests/test_thread_run_messages_pagination.py index f00100cad..00e354a34 100644 --- a/backend/tests/test_thread_run_messages_pagination.py +++ b/backend/tests/test_thread_run_messages_pagination.py @@ -1,15 +1,14 @@ """Tests for paginated GET /api/threads/{thread_id}/runs/{run_id}/messages endpoint.""" + from __future__ import annotations from unittest.mock import AsyncMock, MagicMock -import pytest from _router_auth_helpers import make_authed_test_app from fastapi.testclient import TestClient from app.gateway.routers import thread_runs - # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- @@ -78,7 +77,8 @@ def test_after_seq_forwarded_to_event_store(): response = client.get("/api/threads/thread-3/runs/run-3/messages?after_seq=5") assert response.status_code == 200 event_store.list_messages_by_run.assert_awaited_once_with( - "thread-3", "run-3", + "thread-3", + "run-3", limit=51, # default limit(50) + 1 before_seq=None, after_seq=5, @@ -94,7 +94,8 @@ def test_before_seq_forwarded_to_event_store(): response = client.get("/api/threads/thread-4/runs/run-4/messages?before_seq=10") assert response.status_code == 200 event_store.list_messages_by_run.assert_awaited_once_with( - "thread-4", "run-4", + "thread-4", + "run-4", limit=51, before_seq=10, after_seq=None, @@ -110,7 +111,8 @@ def test_custom_limit_forwarded_to_event_store(): response = client.get("/api/threads/thread-5/runs/run-5/messages?limit=10") assert response.status_code == 200 event_store.list_messages_by_run.assert_awaited_once_with( - "thread-5", "run-5", + "thread-5", + "run-5", limit=11, # 10 + 1 before_seq=None, after_seq=None, diff --git a/backend/tests/test_user_context.py b/backend/tests/test_user_context.py index 8c7cbd13c..111ffb679 100644 --- a/backend/tests/test_user_context.py +++ b/backend/tests/test_user_context.py @@ -10,8 +10,8 @@ from types import SimpleNamespace import pytest from deerflow.runtime.user_context import ( - CurrentUser, DEFAULT_USER_ID, + CurrentUser, get_current_user, get_effective_user_id, require_current_user, @@ -100,6 +100,7 @@ def test_effective_user_id_returns_user_id_when_set(): def test_effective_user_id_coerces_to_str(): """User.id might be a UUID object; must come back as str.""" import uuid + uid = uuid.uuid4() user = SimpleNamespace(id=uid)