mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-06 20:15:17 +02:00
refactor(agents): move middleware package to app/agents/shared (slice 5c)
Relocate the entire new_chat/middleware/ package to the shared kernel as one cohesive unit (it is live shared infrastructure: the multi-agent stack wraps nearly every middleware via multi_agent_chat/middleware/main_agent/*, and anonymous_agent consumes it too). Flip 69 live importers across both the package-path and submodule-path forms. Shims left for the frozen single-agent stack: a package __init__ re-export plus submodule shims for permission, skills_backends, and scoped_model_fallback (the three imported via submodule path by chat_deepagent/subagents). Cycle break: importing shared.middleware previously reached back into new_chat.tools at module load, which dragged in new_chat.__init__ -> chat_deepagent -> the middleware shim -> half-initialized shared.middleware. Made action_log's ToolDefinition import TYPE_CHECKING-only and tool_call_repair's INVALID_TOOL_NAME import function-local. These tools-package back-edges fully resolve in slice 6. Asset note: skills_backends._default_builtin_root now walks to app/agents/new_chat/skills/builtin (the skills/ tree migrates in slice 7).
This commit is contained in:
parent
6f488d9564
commit
227983a104
98 changed files with 1131 additions and 999 deletions
|
|
@ -0,0 +1,91 @@
|
|||
"""Lightweight middleware that loads the anonymous-session document into state.
|
||||
|
||||
Anonymous chats receive a single uploaded document via Redis (no DB row,
|
||||
read-only). This middleware loads it once on the first turn into
|
||||
``state['kb_anon_doc']`` so:
|
||||
|
||||
* :class:`KnowledgeTreeMiddleware` can render the synthetic ``/documents``
|
||||
view without touching the DB.
|
||||
* :class:`KnowledgePriorityMiddleware` skips hybrid search and emits a
|
||||
degenerate priority list.
|
||||
* :class:`KBPostgresBackend` (``als_info`` / ``aread`` / ``_load_file_data``)
|
||||
recognises the synthetic path.
|
||||
|
||||
The middleware is a no-op when ``anon_session_id`` is not provided or when
|
||||
the document is already cached in state.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from langchain.agents.middleware import AgentMiddleware, AgentState
|
||||
from langgraph.runtime import Runtime
|
||||
|
||||
from app.agents.shared.filesystem_state import SurfSenseFilesystemState
|
||||
from app.agents.shared.path_resolver import DOCUMENTS_ROOT, safe_filename
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AnonymousDocumentMiddleware(AgentMiddleware): # type: ignore[type-arg]
|
||||
"""Load the anonymous user's uploaded document from Redis into state."""
|
||||
|
||||
tools = ()
|
||||
state_schema = SurfSenseFilesystemState
|
||||
|
||||
def __init__(self, *, anon_session_id: str | None) -> None:
|
||||
self.anon_session_id = anon_session_id
|
||||
|
||||
async def abefore_agent( # type: ignore[override]
|
||||
self,
|
||||
state: AgentState,
|
||||
runtime: Runtime[Any],
|
||||
) -> dict[str, Any] | None:
|
||||
del runtime
|
||||
if not self.anon_session_id:
|
||||
return None
|
||||
if state.get("kb_anon_doc"):
|
||||
return None
|
||||
|
||||
anon_doc = await self._load_anon_document()
|
||||
if anon_doc is None:
|
||||
return None
|
||||
return {"kb_anon_doc": anon_doc}
|
||||
|
||||
async def _load_anon_document(self) -> dict[str, Any] | None:
|
||||
"""Read ``anon:doc:<session_id>`` from Redis."""
|
||||
try:
|
||||
import redis.asyncio as aioredis # local import to keep cold paths cheap
|
||||
|
||||
from app.config import config
|
||||
|
||||
redis_client = aioredis.from_url(
|
||||
config.REDIS_APP_URL, decode_responses=True
|
||||
)
|
||||
try:
|
||||
redis_key = f"anon:doc:{self.anon_session_id}"
|
||||
data = await redis_client.get(redis_key)
|
||||
if not data:
|
||||
return None
|
||||
payload = json.loads(data)
|
||||
finally:
|
||||
await redis_client.aclose()
|
||||
except Exception as exc:
|
||||
logger.warning("Failed to load anonymous document from Redis: %s", exc)
|
||||
return None
|
||||
|
||||
title = str(payload.get("filename") or "uploaded_document")
|
||||
content = str(payload.get("content") or "")
|
||||
path = f"{DOCUMENTS_ROOT}/{safe_filename(title)}"
|
||||
return {
|
||||
"path": path,
|
||||
"title": title,
|
||||
"content": content,
|
||||
"chunks": [{"chunk_id": -1, "content": content}] if content else [],
|
||||
}
|
||||
|
||||
|
||||
__all__ = ["AnonymousDocumentMiddleware"]
|
||||
Loading…
Add table
Add a link
Reference in a new issue