refactor(agents): consolidate chat runtime infra under chat/runtime

Move the lower-level runtime/infra modules out of multi_agent_chat/shared/
(they were never used by subagents, so they failed the shared-by-all-siblings
rule) and unify them with the already-relocated checkpointer:

  agents/runtime/                      -> agents/chat/runtime/
  mac/shared/errors.py                 -> chat/runtime/errors.py
  mac/shared/llm_config.py             -> chat/runtime/llm_config.py
  mac/shared/prompt_caching.py         -> chat/runtime/prompt_caching.py
  mac/shared/mention_resolver.py       -> chat/runtime/mention_resolver.py
  mac/shared/path_resolver.py          -> chat/runtime/path_resolver.py

These sit below the agent packages: the boundary + agent factory + shared
middleware depend on them, and they import no agent code (acyclic).
This commit is contained in:
CREDO23 2026-06-05 13:19:24 +02:00
parent 7d866a2279
commit f2a61bc0ef
52 changed files with 97 additions and 87 deletions

View file

@ -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__)

View file

@ -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

View file

@ -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 (

View file

@ -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__)

View file

@ -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,

View file

@ -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:

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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,

View file

@ -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:

View file

@ -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

View file

@ -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
"""

View file

@ -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 (

View file

@ -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,

View file

@ -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__)

View file

@ -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.
"""