diff --git a/surfsense_backend/app/routes/new_chat_routes.py b/surfsense_backend/app/routes/new_chat_routes.py
index 476ff2935..da0d239d2 100644
--- a/surfsense_backend/app/routes/new_chat_routes.py
+++ b/surfsense_backend/app/routes/new_chat_routes.py
@@ -706,6 +706,7 @@ async def handle_new_chat(
llm_config_id=llm_config_id,
attachments=request.attachments,
mentioned_document_ids=request.mentioned_document_ids,
+ mentioned_surfsense_doc_ids=request.mentioned_surfsense_doc_ids,
),
media_type="text/event-stream",
headers={
diff --git a/surfsense_backend/app/tasks/chat/stream_new_chat.py b/surfsense_backend/app/tasks/chat/stream_new_chat.py
index 3b87c33f1..25cec6959 100644
--- a/surfsense_backend/app/tasks/chat/stream_new_chat.py
+++ b/surfsense_backend/app/tasks/chat/stream_new_chat.py
@@ -25,7 +25,7 @@ from app.agents.new_chat.llm_config import (
load_agent_config,
load_llm_config_from_yaml,
)
-from app.db import Document
+from app.db import Document, SurfsenseDocsDocument
from app.schemas.new_chat import ChatAttachment
from app.services.connector_service import ConnectorService
from app.services.new_streaming_service import VercelStreamingService
@@ -69,6 +69,29 @@ def format_mentioned_documents_as_context(documents: list[Document]) -> str:
return "\n".join(context_parts)
+def format_mentioned_surfsense_docs_as_context(
+ documents: list[SurfsenseDocsDocument],
+) -> str:
+ """Format mentioned SurfSense documentation as context for the agent."""
+ if not documents:
+ return ""
+
+ context_parts = [""]
+ context_parts.append(
+ "The user has explicitly mentioned the following SurfSense documentation pages. "
+ "These are official documentation about how to use SurfSense and should be used to answer questions about the application."
+ )
+ for i, doc in enumerate(documents, 1):
+ context_parts.append(
+ f""
+ )
+ context_parts.append(f"")
+ context_parts.append("")
+ context_parts.append("")
+
+ return "\n".join(context_parts)
+
+
def extract_todos_from_deepagents(command_output) -> dict:
"""
Extract todos from deepagents' TodoListMiddleware Command output.
@@ -101,6 +124,7 @@ async def stream_new_chat(
llm_config_id: int = -1,
attachments: list[ChatAttachment] | None = None,
mentioned_document_ids: list[int] | None = None,
+ mentioned_surfsense_doc_ids: list[int] | None = None,
) -> AsyncGenerator[str, None]:
"""
Stream chat responses from the new SurfSense deep agent.
@@ -118,6 +142,7 @@ async def stream_new_chat(
messages: Optional chat history from frontend (list of ChatMessage)
attachments: Optional attachments with extracted content
mentioned_document_ids: Optional list of document IDs mentioned with @ in the chat
+ mentioned_surfsense_doc_ids: Optional list of SurfSense doc IDs mentioned with @ in the chat
Yields:
str: SSE formatted response strings
@@ -208,7 +233,17 @@ async def stream_new_chat(
)
mentioned_documents = list(result.scalars().all())
- # Format the user query with context (attachments + mentioned documents)
+ # Fetch mentioned SurfSense docs if any
+ mentioned_surfsense_docs: list[SurfsenseDocsDocument] = []
+ if mentioned_surfsense_doc_ids:
+ result = await session.execute(
+ select(SurfsenseDocsDocument).filter(
+ SurfsenseDocsDocument.id.in_(mentioned_surfsense_doc_ids),
+ )
+ )
+ mentioned_surfsense_docs = list(result.scalars().all())
+
+ # Format the user query with context (attachments + mentioned documents + surfsense docs)
final_query = user_query
context_parts = []
@@ -220,6 +255,11 @@ async def stream_new_chat(
format_mentioned_documents_as_context(mentioned_documents)
)
+ if mentioned_surfsense_docs:
+ context_parts.append(
+ format_mentioned_surfsense_docs_as_context(mentioned_surfsense_docs)
+ )
+
if context_parts:
context = "\n\n".join(context_parts)
final_query = f"{context}\n\n{user_query}"
@@ -296,13 +336,13 @@ async def stream_new_chat(
last_active_step_id = analyze_step_id
# Determine step title and action verb based on context
- if attachments and mentioned_documents:
+ if attachments and (mentioned_documents or mentioned_surfsense_docs):
last_active_step_title = "Analyzing your content"
action_verb = "Reading"
elif attachments:
last_active_step_title = "Reading your content"
action_verb = "Reading"
- elif mentioned_documents:
+ elif mentioned_documents or mentioned_surfsense_docs:
last_active_step_title = "Analyzing referenced content"
action_verb = "Analyzing"
else:
@@ -342,6 +382,19 @@ async def stream_new_chat(
else:
processing_parts.append(f"[{len(doc_names)} documents]")
+ # Add mentioned SurfSense docs inline
+ if mentioned_surfsense_docs:
+ doc_names = []
+ for doc in mentioned_surfsense_docs:
+ title = doc.title
+ if len(title) > 30:
+ title = title[:27] + "..."
+ doc_names.append(title)
+ if len(doc_names) == 1:
+ processing_parts.append(f"[📖 {doc_names[0]}]")
+ else:
+ processing_parts.append(f"[📖 {len(doc_names)} docs]")
+
last_active_step_items = [f"{action_verb}: {' '.join(processing_parts)}"]
yield streaming_service.format_thinking_step(