From a7fde2a48ea39400b24115d6ee3157fb3a5170c3 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Thu, 4 Jun 2026 13:03:15 +0200 Subject: [PATCH] refactor(agents): move filesystem_backends to app/agents/shared (slice 5d) Completes slice 5. filesystem_backends was deferred from 3b because it depends on middleware.{kb_postgres_backend,multi_root_local_folder_backend}; those moved to shared in 5c, so it now relocates cleanly. Flip the 2 non-frozen importers (multi-agent factory + test); a re-export shim remains for the frozen chat_deepagent (build_backend_resolver). --- .../main_agent/runtime/factory.py | 2 +- .../agents/new_chat/filesystem_backends.py | 66 ++----------------- .../app/agents/shared/filesystem_backends.py | 63 ++++++++++++++++++ .../middleware/test_filesystem_backends.py | 2 +- 4 files changed, 71 insertions(+), 62 deletions(-) create mode 100644 surfsense_backend/app/agents/shared/filesystem_backends.py diff --git a/surfsense_backend/app/agents/multi_agent_chat/main_agent/runtime/factory.py b/surfsense_backend/app/agents/multi_agent_chat/main_agent/runtime/factory.py index 8ec1235b7..0f442b026 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/main_agent/runtime/factory.py +++ b/surfsense_backend/app/agents/multi_agent_chat/main_agent/runtime/factory.py @@ -23,7 +23,7 @@ from app.agents.new_chat.connector_searchable_types import ( map_connectors_to_searchable_types, ) from app.agents.shared.feature_flags import AgentFeatureFlags, get_flags -from app.agents.new_chat.filesystem_backends import build_backend_resolver +from app.agents.shared.filesystem_backends import build_backend_resolver from app.agents.shared.filesystem_selection import FilesystemMode, FilesystemSelection from app.agents.shared.llm_config import AgentConfig from app.agents.shared.prompt_caching import apply_litellm_prompt_caching diff --git a/surfsense_backend/app/agents/new_chat/filesystem_backends.py b/surfsense_backend/app/agents/new_chat/filesystem_backends.py index c20eaea5f..1df374e5c 100644 --- a/surfsense_backend/app/agents/new_chat/filesystem_backends.py +++ b/surfsense_backend/app/agents/new_chat/filesystem_backends.py @@ -1,63 +1,9 @@ -"""Filesystem backend resolver for cloud and desktop-local modes.""" +"""Backward-compatible shim. -from __future__ import annotations +Moved to ``app.agents.shared.filesystem_backends``. Re-exported here for the +frozen single-agent stack (``chat_deepagent``) until that stack is retired. +""" -from collections.abc import Callable -from functools import lru_cache +from app.agents.shared.filesystem_backends import build_backend_resolver -from deepagents.backends.protocol import BackendProtocol -from deepagents.backends.state import StateBackend -from langgraph.prebuilt.tool_node import ToolRuntime - -from app.agents.shared.filesystem_selection import FilesystemMode, FilesystemSelection -from app.agents.shared.middleware.kb_postgres_backend import KBPostgresBackend -from app.agents.shared.middleware.multi_root_local_folder_backend import ( - MultiRootLocalFolderBackend, -) - - -@lru_cache(maxsize=64) -def _cached_multi_root_backend( - mounts: tuple[tuple[str, str], ...], -) -> MultiRootLocalFolderBackend: - return MultiRootLocalFolderBackend(mounts) - - -def build_backend_resolver( - selection: FilesystemSelection, - *, - search_space_id: int | None = None, -) -> Callable[[ToolRuntime], BackendProtocol]: - """Create deepagents backend resolver for the selected filesystem mode. - - In cloud mode the resolver returns a fresh :class:`KBPostgresBackend` - bound to the current ``runtime`` so the backend can read staging state - (``staged_dirs``, ``pending_moves``, ``files`` cache, ``kb_anon_doc``, - ``kb_matched_chunk_ids``) for each tool call. When no ``search_space_id`` - is provided, the resolver falls back to :class:`StateBackend` (used by - sub-agents and tests that don't need DB-backed reads). - - Desktop-local mode unchanged. - """ - - if selection.mode == FilesystemMode.DESKTOP_LOCAL_FOLDER and selection.local_mounts: - - def _resolve_local(_runtime: ToolRuntime) -> MultiRootLocalFolderBackend: - mounts = tuple( - (entry.mount_id, entry.root_path) for entry in selection.local_mounts - ) - return _cached_multi_root_backend(mounts) - - return _resolve_local - - if search_space_id is not None: - - def _resolve_kb(runtime: ToolRuntime) -> BackendProtocol: - return KBPostgresBackend(search_space_id, runtime) - - return _resolve_kb - - def _resolve_state(runtime: ToolRuntime) -> StateBackend: - return StateBackend(runtime) - - return _resolve_state +__all__ = ["build_backend_resolver"] diff --git a/surfsense_backend/app/agents/shared/filesystem_backends.py b/surfsense_backend/app/agents/shared/filesystem_backends.py new file mode 100644 index 000000000..c20eaea5f --- /dev/null +++ b/surfsense_backend/app/agents/shared/filesystem_backends.py @@ -0,0 +1,63 @@ +"""Filesystem backend resolver for cloud and desktop-local modes.""" + +from __future__ import annotations + +from collections.abc import Callable +from functools import lru_cache + +from deepagents.backends.protocol import BackendProtocol +from deepagents.backends.state import StateBackend +from langgraph.prebuilt.tool_node import ToolRuntime + +from app.agents.shared.filesystem_selection import FilesystemMode, FilesystemSelection +from app.agents.shared.middleware.kb_postgres_backend import KBPostgresBackend +from app.agents.shared.middleware.multi_root_local_folder_backend import ( + MultiRootLocalFolderBackend, +) + + +@lru_cache(maxsize=64) +def _cached_multi_root_backend( + mounts: tuple[tuple[str, str], ...], +) -> MultiRootLocalFolderBackend: + return MultiRootLocalFolderBackend(mounts) + + +def build_backend_resolver( + selection: FilesystemSelection, + *, + search_space_id: int | None = None, +) -> Callable[[ToolRuntime], BackendProtocol]: + """Create deepagents backend resolver for the selected filesystem mode. + + In cloud mode the resolver returns a fresh :class:`KBPostgresBackend` + bound to the current ``runtime`` so the backend can read staging state + (``staged_dirs``, ``pending_moves``, ``files`` cache, ``kb_anon_doc``, + ``kb_matched_chunk_ids``) for each tool call. When no ``search_space_id`` + is provided, the resolver falls back to :class:`StateBackend` (used by + sub-agents and tests that don't need DB-backed reads). + + Desktop-local mode unchanged. + """ + + if selection.mode == FilesystemMode.DESKTOP_LOCAL_FOLDER and selection.local_mounts: + + def _resolve_local(_runtime: ToolRuntime) -> MultiRootLocalFolderBackend: + mounts = tuple( + (entry.mount_id, entry.root_path) for entry in selection.local_mounts + ) + return _cached_multi_root_backend(mounts) + + return _resolve_local + + if search_space_id is not None: + + def _resolve_kb(runtime: ToolRuntime) -> BackendProtocol: + return KBPostgresBackend(search_space_id, runtime) + + return _resolve_kb + + def _resolve_state(runtime: ToolRuntime) -> StateBackend: + return StateBackend(runtime) + + return _resolve_state diff --git a/surfsense_backend/tests/unit/middleware/test_filesystem_backends.py b/surfsense_backend/tests/unit/middleware/test_filesystem_backends.py index f0d7ab3e9..999102859 100644 --- a/surfsense_backend/tests/unit/middleware/test_filesystem_backends.py +++ b/surfsense_backend/tests/unit/middleware/test_filesystem_backends.py @@ -2,7 +2,7 @@ from pathlib import Path import pytest -from app.agents.new_chat.filesystem_backends import build_backend_resolver +from app.agents.shared.filesystem_backends import build_backend_resolver from app.agents.shared.filesystem_selection import ( ClientPlatform, FilesystemMode,