feat: improved document, folder mentions rendering
Some checks are pending
Build and Push Docker Images / tag_release (push) Waiting to run
Build and Push Docker Images / build (./surfsense_backend, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_backend, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_web, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-24.04-arm, linux/arm64, arm64) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_web, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-latest, linux/amd64, amd64) (push) Blocked by required conditions
Build and Push Docker Images / create_manifest (backend, surfsense-backend) (push) Blocked by required conditions
Build and Push Docker Images / create_manifest (web, surfsense-web) (push) Blocked by required conditions

This commit is contained in:
DESKTOP-RTLN3BA\$punk 2026-05-09 22:15:51 -07:00
parent 28a02a9143
commit c8374e6c5b
59 changed files with 1725 additions and 361 deletions

View file

@ -199,12 +199,16 @@ function pairBundleToolCallIds(
}
/**
* Zod schema for mentioned document info (for type-safe parsing)
* Zod schema for mentioned document info (for type-safe parsing).
*
* ``kind`` defaults to ``"doc"`` so messages persisted before folder
* mentions existed deserialise unchanged.
*/
const MentionedDocumentInfoSchema = z.object({
id: z.number(),
title: z.string(),
document_type: z.string(),
kind: z.union([z.literal("doc"), z.literal("folder")]).optional().default("doc"),
});
const MentionedDocumentsPartSchema = z.object({
@ -913,18 +917,29 @@ export default function NewChatPage() {
hasAttachments: userImages.length > 0,
hasMentionedDocuments:
mentionedDocumentIds.surfsense_doc_ids.length > 0 ||
mentionedDocumentIds.document_ids.length > 0,
mentionedDocumentIds.document_ids.length > 0 ||
mentionedDocumentIds.folder_ids.length > 0,
messageLength: userQuery.length,
});
// Collect unique mentioned docs for display & persistence
// Collect unique mention chips for display & persistence.
// Dedup key is ``kind:document_type:id`` so a folder and a
// doc with the same integer id never collapse into one
// entry. The ``kind`` field is forwarded to the backend
// so the persisted ``mentioned-documents`` content part
// can render the correct chip type on reload.
const allMentionedDocs: MentionedDocumentInfo[] = [];
const seenDocKeys = new Set<string>();
for (const doc of mentionedDocuments) {
const key = `${doc.document_type}:${doc.id}`;
const key = `${doc.kind}:${doc.document_type}:${doc.id}`;
if (seenDocKeys.has(key)) continue;
seenDocKeys.add(key);
allMentionedDocs.push({ id: doc.id, title: doc.title, document_type: doc.document_type });
allMentionedDocs.push({
id: doc.id,
title: doc.title,
document_type: doc.document_type,
kind: doc.kind,
});
}
if (allMentionedDocs.length > 0) {
@ -986,9 +1001,10 @@ export default function NewChatPage() {
// Get mentioned document IDs for context (separate fields for backend)
const hasDocumentIds = mentionedDocumentIds.document_ids.length > 0;
const hasSurfsenseDocIds = mentionedDocumentIds.surfsense_doc_ids.length > 0;
const hasFolderIds = mentionedDocumentIds.folder_ids.length > 0;
// Clear mentioned documents after capturing them
if (hasDocumentIds || hasSurfsenseDocIds) {
if (hasDocumentIds || hasSurfsenseDocIds || hasFolderIds) {
setMentionedDocuments([]);
}
@ -1013,7 +1029,11 @@ export default function NewChatPage() {
mentioned_surfsense_doc_ids: hasSurfsenseDocIds
? mentionedDocumentIds.surfsense_doc_ids
: undefined,
// Full mention metadata so the BE can embed a
mentioned_folder_ids: hasFolderIds
? mentionedDocumentIds.folder_ids
: undefined,
// Full mention metadata (docs + folders, with
// ``kind`` discriminator) so the BE can embed a
// ``mentioned-documents`` ContentPart on the
// persisted user message (replaces the old FE-side
// injection in ``persistUserTurn``).
@ -1023,6 +1043,7 @@ export default function NewChatPage() {
id: d.id,
title: d.title,
document_type: d.document_type,
kind: d.kind,
}))
: undefined,
disabled_tools: disabledTools.length > 0 ? disabledTools : undefined,
@ -1855,6 +1876,23 @@ export default function NewChatPage() {
const selection = await getAgentFilesystemSelection(searchSpaceId, {
localFilesystemEnabled,
});
// Partition the source mentions back into doc/surfsense_doc/folder
// id buckets so the regenerate route can pass them to
// ``stream_new_chat`` and the priority middleware sees the
// same ``[USER-MENTIONED]`` priority entries the original
// turn did. Without this partition the regenerate flow
// silently dropped the agent's mention awareness — same
// architectural bug we fixed on the new-chat path.
const regenerateSurfsenseDocIds = sourceMentionedDocs
.filter((d) => d.kind === "doc" && d.document_type === "SURFSENSE_DOCS")
.map((d) => d.id);
const regenerateDocIds = sourceMentionedDocs
.filter((d) => d.kind === "doc" && d.document_type !== "SURFSENSE_DOCS")
.map((d) => d.id);
const regenerateFolderIds = sourceMentionedDocs
.filter((d) => d.kind === "folder")
.map((d) => d.id);
const requestBody: Record<string, unknown> = {
search_space_id: searchSpaceId,
user_query: newUserQuery,
@ -1862,6 +1900,12 @@ export default function NewChatPage() {
filesystem_mode: selection.filesystem_mode,
client_platform: selection.client_platform,
local_filesystem_mounts: selection.local_filesystem_mounts,
mentioned_document_ids:
regenerateDocIds.length > 0 ? regenerateDocIds : undefined,
mentioned_surfsense_doc_ids:
regenerateSurfsenseDocIds.length > 0 ? regenerateSurfsenseDocIds : undefined,
mentioned_folder_ids:
regenerateFolderIds.length > 0 ? regenerateFolderIds : undefined,
// Full mention metadata for the regenerate-specific
// source list. Only meaningful for edit (the BE only
// re-persists a user row when ``user_query`` is set);
@ -1872,6 +1916,7 @@ export default function NewChatPage() {
id: d.id,
title: d.title,
document_type: d.document_type,
kind: d.kind,
}))
: undefined,
};