mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-29 19:35:20 +02:00
feat(web): enhance chat context and mention handling with connector support
This commit is contained in:
parent
701ae800b4
commit
a41b16b73e
15 changed files with 773 additions and 449 deletions
|
|
@ -64,6 +64,8 @@ class SurfSenseContextSchema:
|
|||
search_space_id: int | None = None
|
||||
mentioned_document_ids: list[int] = field(default_factory=list)
|
||||
mentioned_folder_ids: list[int] = field(default_factory=list)
|
||||
mentioned_connector_ids: list[int] = field(default_factory=list)
|
||||
mentioned_connectors: list[dict[str, object]] = field(default_factory=list)
|
||||
file_operation_contract: FileOperationContractState | None = None
|
||||
turn_id: str | None = None
|
||||
request_id: str | None = None
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ async def resolve_mentions(
|
|||
kind = chip.kind
|
||||
if kind == "folder":
|
||||
chip_folder_ids.append(chip.id)
|
||||
else:
|
||||
elif kind == "doc":
|
||||
chip_doc_ids.append(chip.id)
|
||||
chip_titles_by_id[(kind, chip.id)] = chip.title
|
||||
|
||||
|
|
|
|||
|
|
@ -1771,6 +1771,11 @@ async def handle_new_chat(
|
|||
if request.mentioned_documents
|
||||
else None
|
||||
)
|
||||
mentioned_connectors_payload = (
|
||||
[doc.model_dump() for doc in request.mentioned_connectors]
|
||||
if request.mentioned_connectors
|
||||
else None
|
||||
)
|
||||
|
||||
return StreamingResponse(
|
||||
stream_new_chat(
|
||||
|
|
@ -1782,6 +1787,8 @@ async def handle_new_chat(
|
|||
mentioned_document_ids=request.mentioned_document_ids,
|
||||
mentioned_surfsense_doc_ids=request.mentioned_surfsense_doc_ids,
|
||||
mentioned_folder_ids=request.mentioned_folder_ids,
|
||||
mentioned_connector_ids=request.mentioned_connector_ids,
|
||||
mentioned_connectors=mentioned_connectors_payload,
|
||||
mentioned_documents=mentioned_documents_payload,
|
||||
needs_history_bootstrap=thread.needs_history_bootstrap,
|
||||
thread_visibility=thread.visibility,
|
||||
|
|
@ -2258,6 +2265,11 @@ async def regenerate_response(
|
|||
if request.mentioned_documents
|
||||
else None
|
||||
)
|
||||
mentioned_connectors_payload = (
|
||||
[doc.model_dump() for doc in request.mentioned_connectors]
|
||||
if request.mentioned_connectors
|
||||
else None
|
||||
)
|
||||
try:
|
||||
async for chunk in stream_new_chat(
|
||||
user_query=str(user_query_to_use),
|
||||
|
|
@ -2268,6 +2280,8 @@ async def regenerate_response(
|
|||
mentioned_document_ids=request.mentioned_document_ids,
|
||||
mentioned_surfsense_doc_ids=request.mentioned_surfsense_doc_ids,
|
||||
mentioned_folder_ids=request.mentioned_folder_ids,
|
||||
mentioned_connector_ids=request.mentioned_connector_ids,
|
||||
mentioned_connectors=mentioned_connectors_payload,
|
||||
mentioned_documents=mentioned_documents_payload,
|
||||
checkpoint_id=target_checkpoint_id,
|
||||
needs_history_bootstrap=thread.needs_history_bootstrap,
|
||||
|
|
|
|||
|
|
@ -218,17 +218,20 @@ class MentionedDocumentInfo(BaseModel):
|
|||
id: int
|
||||
title: str = Field(..., min_length=1, max_length=500)
|
||||
document_type: str = Field(..., min_length=1, max_length=100)
|
||||
kind: Literal["doc", "folder"] = Field(
|
||||
kind: Literal["doc", "folder", "connector"] = Field(
|
||||
default="doc",
|
||||
description=(
|
||||
"Discriminator for the chip's referent: ``doc`` is a "
|
||||
"knowledge-base ``Document`` row, ``folder`` is a "
|
||||
"knowledge-base ``Folder`` row. Folders carry the sentinel "
|
||||
"knowledge-base ``Folder`` row, and ``connector`` is a "
|
||||
"concrete connected account. Folders carry the sentinel "
|
||||
"``document_type='FOLDER'`` to keep the frontend dedup key "
|
||||
"``(kind:document_type:id)`` from colliding doc and folder "
|
||||
"ids that happen to share an integer value."
|
||||
),
|
||||
)
|
||||
connector_type: str | None = Field(default=None, max_length=100)
|
||||
account_name: str | None = Field(default=None, max_length=255)
|
||||
|
||||
|
||||
class NewChatRequest(BaseModel):
|
||||
|
|
@ -266,6 +269,18 @@ class NewChatRequest(BaseModel):
|
|||
"a mentioned-documents part."
|
||||
),
|
||||
)
|
||||
mentioned_connector_ids: list[int] | None = Field(
|
||||
default=None,
|
||||
description="Optional concrete connector account IDs the user @-mentioned.",
|
||||
)
|
||||
mentioned_connectors: list[MentionedDocumentInfo] | None = Field(
|
||||
default=None,
|
||||
description=(
|
||||
"Display/context metadata for selected connector accounts. "
|
||||
"Kept separate from document/folder id arrays so tools can "
|
||||
"prefer the exact account the user selected."
|
||||
),
|
||||
)
|
||||
disabled_tools: list[str] | None = (
|
||||
None # Optional list of tool names the user has disabled from the UI
|
||||
)
|
||||
|
|
@ -335,6 +350,8 @@ class RegenerateRequest(BaseModel):
|
|||
"new user message. None means no chip metadata."
|
||||
),
|
||||
)
|
||||
mentioned_connector_ids: list[int] | None = None
|
||||
mentioned_connectors: list[MentionedDocumentInfo] | None = None
|
||||
disabled_tools: list[str] | None = None
|
||||
filesystem_mode: Literal["cloud", "desktop_local_folder"] = "cloud"
|
||||
client_platform: Literal["web", "desktop"] = "web"
|
||||
|
|
|
|||
|
|
@ -137,15 +137,19 @@ def _build_user_content(
|
|||
if doc_id is None or title is None or document_type is None:
|
||||
continue
|
||||
kind_raw = doc.get("kind", "doc")
|
||||
kind = kind_raw if kind_raw in ("doc", "folder") else "doc"
|
||||
normalized.append(
|
||||
{
|
||||
"id": doc_id,
|
||||
"title": str(title),
|
||||
"document_type": str(document_type),
|
||||
"kind": kind,
|
||||
}
|
||||
)
|
||||
kind = kind_raw if kind_raw in ("doc", "folder", "connector") else "doc"
|
||||
item = {
|
||||
"id": doc_id,
|
||||
"title": str(title),
|
||||
"document_type": str(document_type),
|
||||
"kind": kind,
|
||||
}
|
||||
if kind == "connector":
|
||||
connector_type = doc.get("connector_type") or document_type
|
||||
account_name = doc.get("account_name") or title
|
||||
item["connector_type"] = str(connector_type)
|
||||
item["account_name"] = str(account_name)
|
||||
normalized.append(item)
|
||||
if normalized:
|
||||
parts.append({"type": "mentioned-documents", "documents": normalized})
|
||||
return parts
|
||||
|
|
|
|||
|
|
@ -839,6 +839,8 @@ async def stream_new_chat(
|
|||
mentioned_document_ids: list[int] | None = None,
|
||||
mentioned_surfsense_doc_ids: list[int] | None = None,
|
||||
mentioned_folder_ids: list[int] | None = None,
|
||||
mentioned_connector_ids: list[int] | None = None,
|
||||
mentioned_connectors: list[dict[str, Any]] | None = None,
|
||||
mentioned_documents: list[dict[str, Any]] | None = None,
|
||||
checkpoint_id: str | None = None,
|
||||
needs_history_bootstrap: bool = False,
|
||||
|
|
@ -1385,6 +1387,32 @@ async def stream_new_chat(
|
|||
format_mentioned_surfsense_docs_as_context(mentioned_surfsense_docs)
|
||||
)
|
||||
|
||||
if mentioned_connectors:
|
||||
connector_lines = []
|
||||
for connector in mentioned_connectors:
|
||||
if not isinstance(connector, dict):
|
||||
continue
|
||||
connector_id = connector.get("id")
|
||||
connector_type = connector.get("connector_type") or connector.get(
|
||||
"document_type"
|
||||
)
|
||||
account_name = connector.get("account_name") or connector.get("title")
|
||||
if connector_id is None or connector_type is None:
|
||||
continue
|
||||
connector_lines.append(
|
||||
f' - connector_id={connector_id}, connector_type="{connector_type}", '
|
||||
f'account="{account_name or ""}"'
|
||||
)
|
||||
if connector_lines:
|
||||
context_parts.append(
|
||||
"<mentioned_connectors>\n"
|
||||
"The user selected these exact connector accounts with @. "
|
||||
"For read, write, or HITL tool calls involving these services, "
|
||||
"prefer the matching connector_id instead of guessing from available accounts:\n"
|
||||
+ "\n".join(connector_lines)
|
||||
+ "\n</mentioned_connectors>"
|
||||
)
|
||||
|
||||
# Surface report IDs prominently so the LLM doesn't have to
|
||||
# retrieve them from old tool responses in conversation history.
|
||||
if recent_reports:
|
||||
|
|
@ -1778,6 +1806,8 @@ async def stream_new_chat(
|
|||
mentioned_folder_ids=list(
|
||||
accepted_folder_ids or mentioned_folder_ids or []
|
||||
),
|
||||
mentioned_connector_ids=list(mentioned_connector_ids or []),
|
||||
mentioned_connectors=list(mentioned_connectors or []),
|
||||
request_id=request_id,
|
||||
turn_id=stream_result.turn_id,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue