diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/main_agent/middleware/anonymous_document/middleware.py b/surfsense_backend/app/agents/chat/multi_agent_chat/main_agent/middleware/anonymous_document/middleware.py index 6479f99e3..d29c31230 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/main_agent/middleware/anonymous_document/middleware.py +++ b/surfsense_backend/app/agents/chat/multi_agent_chat/main_agent/middleware/anonymous_document/middleware.py @@ -24,13 +24,13 @@ from typing import Any from langchain.agents.middleware import AgentMiddleware, AgentState from langgraph.runtime import Runtime -from app.agents.chat.multi_agent_chat.shared.path_resolver import ( - DOCUMENTS_ROOT, - safe_filename, -) from app.agents.chat.multi_agent_chat.shared.state.filesystem_state import ( SurfSenseFilesystemState, ) +from app.agents.chat.runtime.path_resolver import ( + DOCUMENTS_ROOT, + safe_filename, +) logger = logging.getLogger(__name__) diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/main_agent/middleware/knowledge_tree/middleware.py b/surfsense_backend/app/agents/chat/multi_agent_chat/main_agent/middleware/knowledge_tree/middleware.py index c0461626c..a0c62834a 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/main_agent/middleware/knowledge_tree/middleware.py +++ b/surfsense_backend/app/agents/chat/multi_agent_chat/main_agent/middleware/knowledge_tree/middleware.py @@ -34,15 +34,15 @@ from langgraph.runtime import Runtime from sqlalchemy import select from app.agents.chat.multi_agent_chat.shared.filesystem_selection import FilesystemMode -from app.agents.chat.multi_agent_chat.shared.path_resolver import ( +from app.agents.chat.multi_agent_chat.shared.state.filesystem_state import ( + SurfSenseFilesystemState, +) +from app.agents.chat.runtime.path_resolver import ( DOCUMENTS_ROOT, PathIndex, build_path_index, doc_to_virtual_path, ) -from app.agents.chat.multi_agent_chat.shared.state.filesystem_state import ( - SurfSenseFilesystemState, -) from app.db import Document, shielded_async_session from app.utils.perf import get_perf_logger diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/main_agent/runtime/factory.py b/surfsense_backend/app/agents/chat/multi_agent_chat/main_agent/runtime/factory.py index 01af5e362..d70263841 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/main_agent/runtime/factory.py +++ b/surfsense_backend/app/agents/chat/multi_agent_chat/main_agent/runtime/factory.py @@ -20,13 +20,9 @@ from app.agents.chat.multi_agent_chat.shared.filesystem_selection import ( FilesystemMode, FilesystemSelection, ) -from app.agents.chat.multi_agent_chat.shared.llm_config import AgentConfig from app.agents.chat.multi_agent_chat.shared.middleware.filesystem.backends.resolver import ( build_backend_resolver, ) -from app.agents.chat.multi_agent_chat.shared.prompt_caching import ( - apply_litellm_prompt_caching, -) from app.agents.chat.multi_agent_chat.subagents import ( get_subagents_to_exclude, main_prompt_registry_subagent_lines, @@ -34,6 +30,10 @@ from app.agents.chat.multi_agent_chat.subagents import ( from app.agents.chat.multi_agent_chat.subagents.mcp_tools.index import ( load_mcp_tools_by_connector, ) +from app.agents.chat.runtime.llm_config import AgentConfig +from app.agents.chat.runtime.prompt_caching import ( + apply_litellm_prompt_caching, +) from app.db import ChatVisibility from app.services.connector_service import ConnectorService from app.services.user_tool_allowlist import ( diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/busy_mutex.py b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/busy_mutex.py index 4b397e2ca..f90e2d179 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/busy_mutex.py +++ b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/busy_mutex.py @@ -20,7 +20,7 @@ What this provides: tools can poll to abort cooperatively. The event is reset between turns. Tools should check ``runtime.context.cancel_event.is_set()`` in tight inner loops. -- A typed :class:`~app.agents.chat.multi_agent_chat.shared.errors.BusyError` raised when a +- A typed :class:`~app.agents.chat.runtime.errors.BusyError` raised when a second turn arrives while the lock is held. Note: SurfSense's ``stream_new_chat`` is the call site that should @@ -46,7 +46,7 @@ from langchain.agents.middleware.types import ( from langgraph.config import get_config from langgraph.runtime import Runtime -from app.agents.chat.multi_agent_chat.shared.errors import BusyError +from app.agents.chat.runtime.errors import BusyError logger = logging.getLogger(__name__) diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/backends/kb_postgres.py b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/backends/kb_postgres.py index 20ac0b9b8..7b8aaf2b0 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/backends/kb_postgres.py +++ b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/backends/kb_postgres.py @@ -45,7 +45,7 @@ from sqlalchemy.ext.asyncio import AsyncSession from app.agents.chat.multi_agent_chat.shared.middleware.filesystem.backends.document_xml import ( build_document_xml, ) -from app.agents.chat.multi_agent_chat.shared.path_resolver import ( +from app.agents.chat.runtime.path_resolver import ( DOCUMENTS_ROOT, build_path_index, doc_to_virtual_path, diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/middleware/mode.py b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/middleware/mode.py index b3253b7de..44d69a50a 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/middleware/mode.py +++ b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/middleware/mode.py @@ -3,7 +3,7 @@ from __future__ import annotations from app.agents.chat.multi_agent_chat.shared.filesystem_selection import FilesystemMode -from app.agents.chat.multi_agent_chat.shared.path_resolver import DOCUMENTS_ROOT +from app.agents.chat.runtime.path_resolver import DOCUMENTS_ROOT def is_cloud(mode: FilesystemMode) -> bool: diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/middleware/namespace_policy.py b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/middleware/namespace_policy.py index 368c013e4..1eced41d7 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/middleware/namespace_policy.py +++ b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/middleware/namespace_policy.py @@ -11,10 +11,10 @@ from typing import TYPE_CHECKING from langchain.tools import ToolRuntime -from app.agents.chat.multi_agent_chat.shared.path_resolver import DOCUMENTS_ROOT from app.agents.chat.multi_agent_chat.shared.state.filesystem_state import ( SurfSenseFilesystemState, ) +from app.agents.chat.runtime.path_resolver import DOCUMENTS_ROOT from ..shared.paths import TEMP_PREFIX, basename from .mode import is_cloud diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/tools/cd/index.py b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/tools/cd/index.py index 8203b68b0..0e78e8640 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/tools/cd/index.py +++ b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/tools/cd/index.py @@ -10,10 +10,10 @@ from langchain_core.messages import ToolMessage from langchain_core.tools import BaseTool, StructuredTool from langgraph.types import Command -from app.agents.chat.multi_agent_chat.shared.path_resolver import DOCUMENTS_ROOT from app.agents.chat.multi_agent_chat.shared.state.filesystem_state import ( SurfSenseFilesystemState, ) +from app.agents.chat.runtime.path_resolver import DOCUMENTS_ROOT from ...middleware.async_dispatch import run_async_blocking from ...middleware.path_resolution import resolve_relative diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/tools/mkdir/index.py b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/tools/mkdir/index.py index 85de65ee1..3ea38f525 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/tools/mkdir/index.py +++ b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/tools/mkdir/index.py @@ -11,10 +11,10 @@ from langchain_core.messages import ToolMessage from langchain_core.tools import BaseTool, StructuredTool from langgraph.types import Command -from app.agents.chat.multi_agent_chat.shared.path_resolver import DOCUMENTS_ROOT from app.agents.chat.multi_agent_chat.shared.state.filesystem_state import ( SurfSenseFilesystemState, ) +from app.agents.chat.runtime.path_resolver import DOCUMENTS_ROOT from ...middleware.async_dispatch import run_async_blocking from ...middleware.mode import is_cloud diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/tools/move_file/helpers.py b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/tools/move_file/helpers.py index 8ac9c0501..ded4701f9 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/tools/move_file/helpers.py +++ b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/tools/move_file/helpers.py @@ -11,11 +11,11 @@ from langgraph.types import Command from app.agents.chat.multi_agent_chat.shared.middleware.filesystem.backends.kb_postgres import ( KBPostgresBackend, ) -from app.agents.chat.multi_agent_chat.shared.path_resolver import DOCUMENTS_ROOT from app.agents.chat.multi_agent_chat.shared.state.filesystem_state import ( SurfSenseFilesystemState, ) from app.agents.chat.multi_agent_chat.shared.state.reducers import _CLEAR +from app.agents.chat.runtime.path_resolver import DOCUMENTS_ROOT if TYPE_CHECKING: from ...middleware import SurfSenseFilesystemMiddleware diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/tools/rm/helpers.py b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/tools/rm/helpers.py index 73cb0b6ad..e2e445d08 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/tools/rm/helpers.py +++ b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/tools/rm/helpers.py @@ -15,11 +15,11 @@ from langgraph.types import Command from app.agents.chat.multi_agent_chat.shared.middleware.filesystem.backends.kb_postgres import ( KBPostgresBackend, ) -from app.agents.chat.multi_agent_chat.shared.path_resolver import DOCUMENTS_ROOT from app.agents.chat.multi_agent_chat.shared.state.filesystem_state import ( SurfSenseFilesystemState, ) from app.agents.chat.multi_agent_chat.shared.state.reducers import _CLEAR +from app.agents.chat.runtime.path_resolver import DOCUMENTS_ROOT if TYPE_CHECKING: from ...middleware import SurfSenseFilesystemMiddleware diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/tools/rmdir/helpers.py b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/tools/rmdir/helpers.py index 2f25479c1..b511a8d79 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/tools/rmdir/helpers.py +++ b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/filesystem/tools/rmdir/helpers.py @@ -16,11 +16,11 @@ from langgraph.types import Command from app.agents.chat.multi_agent_chat.shared.middleware.filesystem.backends.kb_postgres import ( KBPostgresBackend, ) -from app.agents.chat.multi_agent_chat.shared.path_resolver import DOCUMENTS_ROOT from app.agents.chat.multi_agent_chat.shared.state.filesystem_state import ( SurfSenseFilesystemState, ) from app.agents.chat.multi_agent_chat.shared.state.reducers import _CLEAR +from app.agents.chat.runtime.path_resolver import DOCUMENTS_ROOT from ...middleware.path_resolution import current_cwd from ...shared.paths import is_ancestor_of diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/kb_persistence.py b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/kb_persistence.py index 164bb370a..9e2d9a8d5 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/kb_persistence.py +++ b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/kb_persistence.py @@ -47,12 +47,6 @@ from sqlalchemy.ext.asyncio import AsyncSession from app.agents.chat.multi_agent_chat.shared.feature_flags import get_flags from app.agents.chat.multi_agent_chat.shared.filesystem_selection import FilesystemMode -from app.agents.chat.multi_agent_chat.shared.path_resolver import ( - DOCUMENTS_ROOT, - parse_documents_path, - safe_folder_segment, - virtual_path_to_doc, -) from app.agents.chat.multi_agent_chat.shared.receipts.receipt import ( Receipt, make_receipt, @@ -61,6 +55,12 @@ from app.agents.chat.multi_agent_chat.shared.state.filesystem_state import ( SurfSenseFilesystemState, ) from app.agents.chat.multi_agent_chat.shared.state.reducers import _CLEAR +from app.agents.chat.runtime.path_resolver import ( + DOCUMENTS_ROOT, + parse_documents_path, + safe_folder_segment, + virtual_path_to_doc, +) from app.db import ( AgentActionLog, Chunk, diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/knowledge_search.py b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/knowledge_search.py index 38e8be7bb..cc716b00f 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/knowledge_search.py +++ b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/knowledge_search.py @@ -47,14 +47,14 @@ from app.agents.chat.multi_agent_chat.shared.date_filters import ( ) from app.agents.chat.multi_agent_chat.shared.feature_flags import get_flags from app.agents.chat.multi_agent_chat.shared.filesystem_selection import FilesystemMode -from app.agents.chat.multi_agent_chat.shared.path_resolver import ( +from app.agents.chat.multi_agent_chat.shared.state.filesystem_state import ( + SurfSenseFilesystemState, +) +from app.agents.chat.runtime.path_resolver import ( PathIndex, build_path_index, doc_to_virtual_path, ) -from app.agents.chat.multi_agent_chat.shared.state.filesystem_state import ( - SurfSenseFilesystemState, -) from app.db import ( NATIVE_TO_LEGACY_DOCTYPE, Chunk, diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/permissions/deny.py b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/permissions/deny.py index 0811aed77..4997e1716 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/permissions/deny.py +++ b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/permissions/deny.py @@ -11,8 +11,8 @@ from typing import Any from langchain_core.messages import ToolMessage -from app.agents.chat.multi_agent_chat.shared.errors import StreamingError from app.agents.chat.multi_agent_chat.shared.permissions import Rule +from app.agents.chat.runtime.errors import StreamingError def build_deny_message(tool_call: dict[str, Any], rule: Rule) -> ToolMessage: diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/permissions/middleware/core.py b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/permissions/middleware/core.py index 4f5f551fd..dc25c94c6 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/permissions/middleware/core.py +++ b/surfsense_backend/app/agents/chat/multi_agent_chat/shared/middleware/permissions/middleware/core.py @@ -26,8 +26,8 @@ from langchain_core.messages import AIMessage, ToolMessage from langchain_core.tools import BaseTool from langgraph.runtime import Runtime -from app.agents.chat.multi_agent_chat.shared.errors import CorrectedError, RejectedError from app.agents.chat.multi_agent_chat.shared.permissions import Ruleset +from app.agents.chat.runtime.errors import CorrectedError, RejectedError from app.services.user_tool_allowlist import TrustedToolSaver from ..ask.edit import merge_edited_args diff --git a/surfsense_backend/app/agents/chat/runtime/__init__.py b/surfsense_backend/app/agents/chat/runtime/__init__.py new file mode 100644 index 000000000..9cc63f289 --- /dev/null +++ b/surfsense_backend/app/agents/chat/runtime/__init__.py @@ -0,0 +1,16 @@ +"""Lower-level runtime infrastructure for the chat agents. + +Modules here are the foundation layer used to *run* chat agents: wired by the +boundary (routes/tasks) and/or imported by the agent factory + shared +middleware, but never part of any single agent's domain logic. Because they sit +below the agent packages, both the boundary and the agents may depend on them +(forward dependency), while they never import agent code. + +Contents: +- ``checkpointer`` LangGraph Postgres checkpoint saver (boundary lifespan) +- ``llm_config`` LLM provider/model configuration resolution +- ``prompt_caching`` LiteLLM prompt-caching configuration +- ``errors`` agent-runtime error contracts (raised by MW, caught at boundary) +- ``path_resolver`` filesystem path resolution helpers +- ``mention_resolver`` @-mention resolution helpers +""" diff --git a/surfsense_backend/app/agents/runtime/checkpointer.py b/surfsense_backend/app/agents/chat/runtime/checkpointer.py similarity index 100% rename from surfsense_backend/app/agents/runtime/checkpointer.py rename to surfsense_backend/app/agents/chat/runtime/checkpointer.py diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/errors.py b/surfsense_backend/app/agents/chat/runtime/errors.py similarity index 100% rename from surfsense_backend/app/agents/chat/multi_agent_chat/shared/errors.py rename to surfsense_backend/app/agents/chat/runtime/errors.py diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/llm_config.py b/surfsense_backend/app/agents/chat/runtime/llm_config.py similarity index 99% rename from surfsense_backend/app/agents/chat/multi_agent_chat/shared/llm_config.py rename to surfsense_backend/app/agents/chat/runtime/llm_config.py index 453942b9e..c5f929ec2 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/llm_config.py +++ b/surfsense_backend/app/agents/chat/runtime/llm_config.py @@ -27,7 +27,7 @@ from litellm import get_model_info from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession -from app.agents.chat.multi_agent_chat.shared.prompt_caching import ( +from app.agents.chat.runtime.prompt_caching import ( apply_litellm_prompt_caching, ) from app.services.llm_router_service import ( diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/mention_resolver.py b/surfsense_backend/app/agents/chat/runtime/mention_resolver.py similarity index 99% rename from surfsense_backend/app/agents/chat/multi_agent_chat/shared/mention_resolver.py rename to surfsense_backend/app/agents/chat/runtime/mention_resolver.py index 83a2c6c69..a47ed8f36 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/mention_resolver.py +++ b/surfsense_backend/app/agents/chat/runtime/mention_resolver.py @@ -36,7 +36,7 @@ from dataclasses import dataclass, field from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession -from app.agents.chat.multi_agent_chat.shared.path_resolver import ( +from app.agents.chat.runtime.path_resolver import ( DOCUMENTS_ROOT, build_path_index, doc_to_virtual_path, diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/path_resolver.py b/surfsense_backend/app/agents/chat/runtime/path_resolver.py similarity index 100% rename from surfsense_backend/app/agents/chat/multi_agent_chat/shared/path_resolver.py rename to surfsense_backend/app/agents/chat/runtime/path_resolver.py diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/prompt_caching.py b/surfsense_backend/app/agents/chat/runtime/prompt_caching.py similarity index 99% rename from surfsense_backend/app/agents/chat/multi_agent_chat/shared/prompt_caching.py rename to surfsense_backend/app/agents/chat/runtime/prompt_caching.py index 3534b37bf..da0007b1e 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/shared/prompt_caching.py +++ b/surfsense_backend/app/agents/chat/runtime/prompt_caching.py @@ -68,7 +68,7 @@ from typing import TYPE_CHECKING, Any from langchain_core.language_models import BaseChatModel if TYPE_CHECKING: - from app.agents.chat.multi_agent_chat.shared.llm_config import AgentConfig + from app.agents.chat.runtime.llm_config import AgentConfig logger = logging.getLogger(__name__) diff --git a/surfsense_backend/app/agents/runtime/__init__.py b/surfsense_backend/app/agents/runtime/__init__.py deleted file mode 100644 index eee94d84c..000000000 --- a/surfsense_backend/app/agents/runtime/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -"""Agent runtime infrastructure wired by the boundary (not agent code). - -Modules here are cross-agent infra used to *run* agents (e.g. the LangGraph -Postgres checkpointer), as opposed to ``app/agents/shared/`` which holds code -imported by 2+ sibling agent packages. -""" diff --git a/surfsense_backend/app/app.py b/surfsense_backend/app/app.py index 6178bc2c5..e9ffa74d7 100644 --- a/surfsense_backend/app/app.py +++ b/surfsense_backend/app/app.py @@ -23,7 +23,7 @@ from starlette.requests import Request as StarletteRequest from starlette.responses import Response as StarletteResponse from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware -from app.agents.runtime.checkpointer import ( +from app.agents.chat.runtime.checkpointer import ( close_checkpointer, setup_checkpointer_tables, ) diff --git a/surfsense_backend/app/automations/actions/builtin/agent_task/invoke.py b/surfsense_backend/app/automations/actions/builtin/agent_task/invoke.py index 15ad3e3be..aa96e4f6e 100644 --- a/surfsense_backend/app/automations/actions/builtin/agent_task/invoke.py +++ b/surfsense_backend/app/automations/actions/builtin/agent_task/invoke.py @@ -11,7 +11,7 @@ from langgraph.types import Command from sqlalchemy.ext.asyncio import AsyncSession from app.agents.chat.multi_agent_chat import create_multi_agent_chat_deep_agent -from app.agents.chat.multi_agent_chat.shared.mention_resolver import ( +from app.agents.chat.runtime.mention_resolver import ( resolve_mentions, substitute_in_text, ) diff --git a/surfsense_backend/app/automations/services/model_policy.py b/surfsense_backend/app/automations/services/model_policy.py index 1981653ea..7e3e46b61 100644 --- a/surfsense_backend/app/automations/services/model_policy.py +++ b/surfsense_backend/app/automations/services/model_policy.py @@ -39,7 +39,7 @@ def _is_premium_global(kind: ModelKind, config_id: int) -> bool: cfg: dict | None = None if kind == "llm": - from app.agents.chat.multi_agent_chat.shared.llm_config import ( + from app.agents.chat.runtime.llm_config import ( load_global_llm_config_by_id, ) diff --git a/surfsense_backend/app/routes/anonymous_chat_routes.py b/surfsense_backend/app/routes/anonymous_chat_routes.py index 982814dee..ad3277375 100644 --- a/surfsense_backend/app/routes/anonymous_chat_routes.py +++ b/surfsense_backend/app/routes/anonymous_chat_routes.py @@ -236,7 +236,7 @@ async def stream_anonymous_chat( detail="No-login mode is not enabled.", ) - from app.agents.chat.multi_agent_chat.shared.llm_config import ( + from app.agents.chat.runtime.llm_config import ( AgentConfig, create_chat_litellm_from_agent_config, ) @@ -352,7 +352,7 @@ async def stream_anonymous_chat( from langchain_core.messages import AIMessage, HumanMessage from app.agents.chat.anonymous_chat import create_anonymous_chat_agent - from app.agents.runtime.checkpointer import get_checkpointer + from app.agents.chat.runtime.checkpointer import get_checkpointer from app.db import shielded_async_session from app.services.new_streaming_service import VercelStreamingService from app.services.token_tracking_service import start_turn diff --git a/surfsense_backend/app/routes/documents_routes.py b/surfsense_backend/app/routes/documents_routes.py index 366ddad74..8036fae10 100644 --- a/surfsense_backend/app/routes/documents_routes.py +++ b/surfsense_backend/app/routes/documents_routes.py @@ -7,7 +7,7 @@ from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.future import select from sqlalchemy.orm import selectinload -from app.agents.chat.multi_agent_chat.shared.path_resolver import virtual_path_to_doc +from app.agents.chat.runtime.path_resolver import virtual_path_to_doc from app.db import ( Chunk, Document, diff --git a/surfsense_backend/app/routes/new_chat_routes.py b/surfsense_backend/app/routes/new_chat_routes.py index 5273b9d17..5b63ce52b 100644 --- a/surfsense_backend/app/routes/new_chat_routes.py +++ b/surfsense_backend/app/routes/new_chat_routes.py @@ -1934,7 +1934,7 @@ async def regenerate_response( """ from langchain_core.messages import HumanMessage - from app.agents.runtime.checkpointer import get_checkpointer + from app.agents.chat.runtime.checkpointer import get_checkpointer try: # Verify thread exists and user has permission diff --git a/surfsense_backend/app/services/llm_service.py b/surfsense_backend/app/services/llm_service.py index b86562a20..e5e34d652 100644 --- a/surfsense_backend/app/services/llm_service.py +++ b/surfsense_backend/app/services/llm_service.py @@ -204,7 +204,7 @@ async def validate_llm_config( if litellm_params: litellm_kwargs.update(litellm_params) - from app.agents.chat.multi_agent_chat.shared.llm_config import ( + from app.agents.chat.runtime.llm_config import ( SanitizedChatLiteLLM, ) @@ -381,7 +381,7 @@ async def get_search_space_llm_instance( if disable_streaming: litellm_kwargs["disable_streaming"] = True - from app.agents.chat.multi_agent_chat.shared.llm_config import ( + from app.agents.chat.runtime.llm_config import ( SanitizedChatLiteLLM, ) @@ -462,7 +462,7 @@ async def get_search_space_llm_instance( if disable_streaming: litellm_kwargs["disable_streaming"] = True - from app.agents.chat.multi_agent_chat.shared.llm_config import ( + from app.agents.chat.runtime.llm_config import ( SanitizedChatLiteLLM, ) @@ -586,7 +586,7 @@ async def get_vision_llm( if global_cfg.get("litellm_params"): litellm_kwargs.update(global_cfg["litellm_params"]) - from app.agents.chat.multi_agent_chat.shared.llm_config import ( + from app.agents.chat.runtime.llm_config import ( SanitizedChatLiteLLM, ) @@ -642,7 +642,7 @@ async def get_vision_llm( if vision_cfg.litellm_params: litellm_kwargs.update(vision_cfg.litellm_params) - from app.agents.chat.multi_agent_chat.shared.llm_config import ( + from app.agents.chat.runtime.llm_config import ( SanitizedChatLiteLLM, ) @@ -689,7 +689,7 @@ def get_planner_llm() -> ChatLiteLLM | None: Callers MUST fall back to their chat LLM when this returns ``None`` so deployments without a planner config keep working unchanged. """ - from app.agents.chat.multi_agent_chat.shared.llm_config import ( + from app.agents.chat.runtime.llm_config import ( create_chat_litellm_from_config, ) diff --git a/surfsense_backend/app/services/provider_capabilities.py b/surfsense_backend/app/services/provider_capabilities.py index 36e0299eb..f094c9954 100644 --- a/surfsense_backend/app/services/provider_capabilities.py +++ b/surfsense_backend/app/services/provider_capabilities.py @@ -53,7 +53,7 @@ logger = logging.getLogger(__name__) # # Owned here because ``app.services.provider_capabilities`` is the # only edge that's safe to call from ``app.config``'s YAML loader at -# class-body init time. ``app.agents.chat.multi_agent_chat.shared.llm_config`` re-exports +# class-body init time. ``app.agents.chat.runtime.llm_config`` re-exports # this constant under the historical ``PROVIDER_MAP`` name; placing the # map there directly would re-introduce the # ``app.config -> ... -> deliverables/tools/generate_image -> diff --git a/surfsense_backend/app/services/revert_service.py b/surfsense_backend/app/services/revert_service.py index 59b4c0072..6db5e2604 100644 --- a/surfsense_backend/app/services/revert_service.py +++ b/surfsense_backend/app/services/revert_service.py @@ -38,7 +38,7 @@ from typing import Any, Literal from sqlalchemy import delete, select from sqlalchemy.ext.asyncio import AsyncSession -from app.agents.chat.multi_agent_chat.shared.path_resolver import ( +from app.agents.chat.runtime.path_resolver import ( DOCUMENTS_ROOT, safe_filename, safe_folder_segment, diff --git a/surfsense_backend/app/tasks/chat/streaming/agent/builder.py b/surfsense_backend/app/tasks/chat/streaming/agent/builder.py index 31662b20a..dcbd37521 100644 --- a/surfsense_backend/app/tasks/chat/streaming/agent/builder.py +++ b/surfsense_backend/app/tasks/chat/streaming/agent/builder.py @@ -12,7 +12,7 @@ from typing import Any from app.agents.chat.multi_agent_chat.shared.filesystem_selection import ( FilesystemSelection, ) -from app.agents.chat.multi_agent_chat.shared.llm_config import AgentConfig +from app.agents.chat.runtime.llm_config import AgentConfig from app.db import ChatVisibility from app.services.connector_service import ConnectorService diff --git a/surfsense_backend/app/tasks/chat/streaming/errors/classifier.py b/surfsense_backend/app/tasks/chat/streaming/errors/classifier.py index bb0c10084..6ae5af40f 100644 --- a/surfsense_backend/app/tasks/chat/streaming/errors/classifier.py +++ b/surfsense_backend/app/tasks/chat/streaming/errors/classifier.py @@ -7,11 +7,11 @@ import logging import time from typing import Any, Literal -from app.agents.chat.multi_agent_chat.shared.errors import BusyError from app.agents.chat.multi_agent_chat.shared.middleware.busy_mutex import ( get_cancel_state, is_cancel_requested, ) +from app.agents.chat.runtime.errors import BusyError TURN_CANCELLING_INITIAL_DELAY_MS = 200 TURN_CANCELLING_BACKOFF_FACTOR = 2 diff --git a/surfsense_backend/app/tasks/chat/streaming/flows/new_chat/input_state.py b/surfsense_backend/app/tasks/chat/streaming/flows/new_chat/input_state.py index 9ee102e2e..ae301238f 100644 --- a/surfsense_backend/app/tasks/chat/streaming/flows/new_chat/input_state.py +++ b/surfsense_backend/app/tasks/chat/streaming/flows/new_chat/input_state.py @@ -29,7 +29,7 @@ from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.future import select from app.agents.chat.multi_agent_chat.shared.filesystem_selection import FilesystemMode -from app.agents.chat.multi_agent_chat.shared.mention_resolver import ( +from app.agents.chat.runtime.mention_resolver import ( resolve_mentions, substitute_in_text, ) diff --git a/surfsense_backend/app/tasks/chat/streaming/flows/new_chat/llm_capability.py b/surfsense_backend/app/tasks/chat/streaming/flows/new_chat/llm_capability.py index d7b830e65..69b9f4ab8 100644 --- a/surfsense_backend/app/tasks/chat/streaming/flows/new_chat/llm_capability.py +++ b/surfsense_backend/app/tasks/chat/streaming/flows/new_chat/llm_capability.py @@ -15,7 +15,7 @@ tells the user what to change. from __future__ import annotations -from app.agents.chat.multi_agent_chat.shared.llm_config import AgentConfig +from app.agents.chat.runtime.llm_config import AgentConfig from app.observability import otel as ot diff --git a/surfsense_backend/app/tasks/chat/streaming/flows/new_chat/title_gen.py b/surfsense_backend/app/tasks/chat/streaming/flows/new_chat/title_gen.py index f85b56b28..fe3d210bb 100644 --- a/surfsense_backend/app/tasks/chat/streaming/flows/new_chat/title_gen.py +++ b/surfsense_backend/app/tasks/chat/streaming/flows/new_chat/title_gen.py @@ -30,7 +30,7 @@ from app.prompts import TITLE_GENERATION_PROMPT from app.services.new_streaming_service import VercelStreamingService if TYPE_CHECKING: - from app.agents.chat.multi_agent_chat.shared.llm_config import AgentConfig + from app.agents.chat.runtime.llm_config import AgentConfig from app.services.token_tracking_service import TokenAccumulator diff --git a/surfsense_backend/app/tasks/chat/streaming/flows/shared/llm_bundle.py b/surfsense_backend/app/tasks/chat/streaming/flows/shared/llm_bundle.py index 09eb4efcd..7e2bc950b 100644 --- a/surfsense_backend/app/tasks/chat/streaming/flows/shared/llm_bundle.py +++ b/surfsense_backend/app/tasks/chat/streaming/flows/shared/llm_bundle.py @@ -14,7 +14,7 @@ from typing import Any from sqlalchemy.ext.asyncio import AsyncSession -from app.agents.chat.multi_agent_chat.shared.llm_config import ( +from app.agents.chat.runtime.llm_config import ( AgentConfig, create_chat_litellm_from_agent_config, create_chat_litellm_from_config, diff --git a/surfsense_backend/app/tasks/chat/streaming/flows/shared/pre_stream_setup.py b/surfsense_backend/app/tasks/chat/streaming/flows/shared/pre_stream_setup.py index 4376046d6..f717cb325 100644 --- a/surfsense_backend/app/tasks/chat/streaming/flows/shared/pre_stream_setup.py +++ b/surfsense_backend/app/tasks/chat/streaming/flows/shared/pre_stream_setup.py @@ -4,7 +4,7 @@ from __future__ import annotations from sqlalchemy.ext.asyncio import AsyncSession -from app.agents.runtime.checkpointer import get_checkpointer +from app.agents.chat.runtime.checkpointer import get_checkpointer from app.db import SearchSourceConnectorType from app.services.connector_service import ConnectorService @@ -33,7 +33,7 @@ async def setup_connector_and_firecrawl( async def get_chat_checkpointer(): """Resolve the PostgreSQL checkpointer for persistent conversation memory. - Thin wrapper around ``app.agents.runtime.checkpointer.get_checkpointer`` so + Thin wrapper around ``app.agents.chat.runtime.checkpointer.get_checkpointer`` so flow orchestrators can rely on a streaming-local symbol and we have a hook point if the checkpointer source ever needs to vary per flow. """ diff --git a/surfsense_backend/app/tasks/chat/streaming/flows/shared/premium_quota.py b/surfsense_backend/app/tasks/chat/streaming/flows/shared/premium_quota.py index fcabbc6d5..6c08cb29f 100644 --- a/surfsense_backend/app/tasks/chat/streaming/flows/shared/premium_quota.py +++ b/surfsense_backend/app/tasks/chat/streaming/flows/shared/premium_quota.py @@ -19,7 +19,7 @@ from dataclasses import dataclass from typing import TYPE_CHECKING from uuid import UUID -from app.agents.chat.multi_agent_chat.shared.llm_config import AgentConfig +from app.agents.chat.runtime.llm_config import AgentConfig from app.db import shielded_async_session if TYPE_CHECKING: diff --git a/surfsense_backend/app/tasks/chat/streaming/flows/shared/terminal_error.py b/surfsense_backend/app/tasks/chat/streaming/flows/shared/terminal_error.py index 7e627e64e..126149cc1 100644 --- a/surfsense_backend/app/tasks/chat/streaming/flows/shared/terminal_error.py +++ b/surfsense_backend/app/tasks/chat/streaming/flows/shared/terminal_error.py @@ -14,7 +14,7 @@ import traceback from collections.abc import Iterator from typing import Any, Literal -from app.agents.chat.multi_agent_chat.shared.errors import BusyError +from app.agents.chat.runtime.errors import BusyError from app.observability import metrics as ot_metrics, otel as ot from app.services.new_streaming_service import VercelStreamingService from app.tasks.chat.streaming.errors.classifier import classify_stream_exception diff --git a/surfsense_backend/tests/e2e/run_backend.py b/surfsense_backend/tests/e2e/run_backend.py index 860bf7e68..f525cc5f7 100644 --- a/surfsense_backend/tests/e2e/run_backend.py +++ b/surfsense_backend/tests/e2e/run_backend.py @@ -239,11 +239,11 @@ def _patch_llm_bindings() -> None: chat_targets = [ ( - "app.agents.chat.multi_agent_chat.shared.llm_config.create_chat_litellm_from_agent_config", + "app.agents.chat.runtime.llm_config.create_chat_litellm_from_agent_config", fake_create_chat_litellm_from_agent_config, ), ( - "app.agents.chat.multi_agent_chat.shared.llm_config.create_chat_litellm_from_config", + "app.agents.chat.runtime.llm_config.create_chat_litellm_from_config", fake_create_chat_litellm_from_config, ), ( diff --git a/surfsense_backend/tests/e2e/run_celery.py b/surfsense_backend/tests/e2e/run_celery.py index fa9b1aa25..11ec628b9 100644 --- a/surfsense_backend/tests/e2e/run_celery.py +++ b/surfsense_backend/tests/e2e/run_celery.py @@ -212,11 +212,11 @@ def _patch_llm_bindings() -> None: chat_targets = [ ( - "app.agents.chat.multi_agent_chat.shared.llm_config.create_chat_litellm_from_agent_config", + "app.agents.chat.runtime.llm_config.create_chat_litellm_from_agent_config", fake_create_chat_litellm_from_agent_config, ), ( - "app.agents.chat.multi_agent_chat.shared.llm_config.create_chat_litellm_from_config", + "app.agents.chat.runtime.llm_config.create_chat_litellm_from_config", fake_create_chat_litellm_from_config, ), ( diff --git a/surfsense_backend/tests/unit/agents/new_chat/test_busy_mutex.py b/surfsense_backend/tests/unit/agents/new_chat/test_busy_mutex.py index 4553371ff..c9ce67206 100644 --- a/surfsense_backend/tests/unit/agents/new_chat/test_busy_mutex.py +++ b/surfsense_backend/tests/unit/agents/new_chat/test_busy_mutex.py @@ -4,7 +4,6 @@ from __future__ import annotations import pytest -from app.agents.chat.multi_agent_chat.shared.errors import BusyError from app.agents.chat.multi_agent_chat.shared.middleware.busy_mutex import ( BusyMutexMiddleware, end_turn, @@ -14,6 +13,7 @@ from app.agents.chat.multi_agent_chat.shared.middleware.busy_mutex import ( request_cancel, reset_cancel, ) +from app.agents.chat.runtime.errors import BusyError pytestmark = pytest.mark.unit diff --git a/surfsense_backend/tests/unit/agents/new_chat/test_mention_resolver.py b/surfsense_backend/tests/unit/agents/new_chat/test_mention_resolver.py index b22ca2775..4130c9d4e 100644 --- a/surfsense_backend/tests/unit/agents/new_chat/test_mention_resolver.py +++ b/surfsense_backend/tests/unit/agents/new_chat/test_mention_resolver.py @@ -15,14 +15,14 @@ from unittest.mock import AsyncMock, MagicMock import pytest -from app.agents.chat.multi_agent_chat.shared import mention_resolver -from app.agents.chat.multi_agent_chat.shared.mention_resolver import ( +from app.agents.chat.runtime import mention_resolver +from app.agents.chat.runtime.mention_resolver import ( ResolvedMention, ResolvedMentionSet, resolve_mentions, substitute_in_text, ) -from app.agents.chat.multi_agent_chat.shared.path_resolver import ( +from app.agents.chat.runtime.path_resolver import ( DOCUMENTS_ROOT, PathIndex, ) diff --git a/surfsense_backend/tests/unit/agents/new_chat/test_path_resolver.py b/surfsense_backend/tests/unit/agents/new_chat/test_path_resolver.py index f73e06f37..2617bff8e 100644 --- a/surfsense_backend/tests/unit/agents/new_chat/test_path_resolver.py +++ b/surfsense_backend/tests/unit/agents/new_chat/test_path_resolver.py @@ -7,7 +7,7 @@ from unittest.mock import AsyncMock, MagicMock import pytest -from app.agents.chat.multi_agent_chat.shared.path_resolver import ( +from app.agents.chat.runtime.path_resolver import ( DOCUMENTS_ROOT, PathIndex, doc_to_virtual_path, diff --git a/surfsense_backend/tests/unit/agents/new_chat/test_prompt_caching.py b/surfsense_backend/tests/unit/agents/new_chat/test_prompt_caching.py index 45219ff17..6fbe39349 100644 --- a/surfsense_backend/tests/unit/agents/new_chat/test_prompt_caching.py +++ b/surfsense_backend/tests/unit/agents/new_chat/test_prompt_caching.py @@ -1,5 +1,5 @@ r"""Tests for ``apply_litellm_prompt_caching`` in -:mod:`app.agents.chat.multi_agent_chat.shared.prompt_caching`. +:mod:`app.agents.chat.runtime.prompt_caching`. The helper replaces the legacy ``AnthropicPromptCachingMiddleware`` (which never activated for our LiteLLM stack) with LiteLLM-native multi-provider @@ -34,8 +34,8 @@ from typing import Any import pytest -from app.agents.chat.multi_agent_chat.shared.llm_config import AgentConfig -from app.agents.chat.multi_agent_chat.shared.prompt_caching import ( +from app.agents.chat.runtime.llm_config import AgentConfig +from app.agents.chat.runtime.prompt_caching import ( apply_litellm_prompt_caching, ) diff --git a/surfsense_backend/tests/unit/automations/services/test_model_policy.py b/surfsense_backend/tests/unit/automations/services/test_model_policy.py index fab4abd65..8e0806151 100644 --- a/surfsense_backend/tests/unit/automations/services/test_model_policy.py +++ b/surfsense_backend/tests/unit/automations/services/test_model_policy.py @@ -44,7 +44,7 @@ def patched_globals(monkeypatch: pytest.MonkeyPatch): -2: {"id": -2, "billing_tier": "free"}, } monkeypatch.setattr( - "app.agents.chat.multi_agent_chat.shared.llm_config.load_global_llm_config_by_id", + "app.agents.chat.runtime.llm_config.load_global_llm_config_by_id", lambda cid: llm_configs.get(cid), ) diff --git a/surfsense_backend/tests/unit/middleware/test_knowledge_tree.py b/surfsense_backend/tests/unit/middleware/test_knowledge_tree.py index 78986ef31..c14eca080 100644 --- a/surfsense_backend/tests/unit/middleware/test_knowledge_tree.py +++ b/surfsense_backend/tests/unit/middleware/test_knowledge_tree.py @@ -12,7 +12,7 @@ from __future__ import annotations from app.agents.chat.multi_agent_chat.main_agent.middleware.knowledge_tree.middleware import ( KnowledgeTreeMiddleware, ) -from app.agents.chat.multi_agent_chat.shared.path_resolver import DOCUMENTS_ROOT +from app.agents.chat.runtime.path_resolver import DOCUMENTS_ROOT def _compute(folder_paths: list[str], doc_paths: list[str]) -> set[str]: @@ -88,7 +88,7 @@ class TestFormatTreeRendering: folder_paths: list[str], doc_specs: list[dict], ) -> str: - from app.agents.chat.multi_agent_chat.shared.path_resolver import PathIndex + from app.agents.chat.runtime.path_resolver import PathIndex index = PathIndex( folder_paths={i + 1: p for i, p in enumerate(folder_paths)}, diff --git a/surfsense_backend/tests/unit/services/test_supports_image_input.py b/surfsense_backend/tests/unit/services/test_supports_image_input.py index ab23ab5a5..fabb3587a 100644 --- a/surfsense_backend/tests/unit/services/test_supports_image_input.py +++ b/surfsense_backend/tests/unit/services/test_supports_image_input.py @@ -227,7 +227,7 @@ global_llm_configs: def test_agent_config_from_yaml_explicit_overrides_resolver(): - from app.agents.chat.multi_agent_chat.shared.llm_config import AgentConfig + from app.agents.chat.runtime.llm_config import AgentConfig cfg_text_only = AgentConfig.from_yaml_config( { @@ -256,7 +256,7 @@ def test_agent_config_from_yaml_explicit_overrides_resolver(): def test_agent_config_from_yaml_unannotated_uses_resolver(): """Without an explicit YAML key, AgentConfig defers to the catalog resolver — for ``gpt-4o`` LiteLLM's map says supports_vision=True.""" - from app.agents.chat.multi_agent_chat.shared.llm_config import AgentConfig + from app.agents.chat.runtime.llm_config import AgentConfig cfg = AgentConfig.from_yaml_config( { @@ -275,7 +275,7 @@ def test_agent_config_auto_mode_supports_image_input(): so users can keep their selection on Auto with a vision-capable deployment somewhere in the pool. The router's own `allowed_fails` handles non-vision deployments via fallback.""" - from app.agents.chat.multi_agent_chat.shared.llm_config import AgentConfig + from app.agents.chat.runtime.llm_config import AgentConfig auto = AgentConfig.from_auto_mode() assert auto.supports_image_input is True diff --git a/surfsense_backend/tests/unit/services/test_vision_llm_api_base_defense.py b/surfsense_backend/tests/unit/services/test_vision_llm_api_base_defense.py index 661766bb9..5e3aa6eda 100644 --- a/surfsense_backend/tests/unit/services/test_vision_llm_api_base_defense.py +++ b/surfsense_backend/tests/unit/services/test_vision_llm_api_base_defense.py @@ -61,7 +61,7 @@ async def test_get_vision_llm_global_openrouter_sets_api_base(): return_value=cfg, ), patch( - "app.agents.chat.multi_agent_chat.shared.llm_config.SanitizedChatLiteLLM", + "app.agents.chat.runtime.llm_config.SanitizedChatLiteLLM", new=FakeSanitized, ), ):