From 5ecdfae8a9cb0feac92e6a472ad9bb7328e68cda Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Tue, 13 Jan 2026 02:50:56 +0200 Subject: [PATCH] add surfsense docs context formatting in stream_new_chat --- .../app/routes/new_chat_routes.py | 1 + .../app/tasks/chat/stream_new_chat.py | 61 +++++++++++++++++-- 2 files changed, 58 insertions(+), 4 deletions(-) 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(