mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-31 19:45:15 +02:00
refactor: remove search_surfsense_docs tool and related references
- Deleted the `search_surfsense_docs` tool and its associated files, streamlining the agent's toolset. - Updated various components and prompts to remove references to the now-removed tool, ensuring consistency across the codebase. - Adjusted documentation to direct users to the SurfSense documentation link for product-related queries instead.
This commit is contained in:
parent
9b9e6828c7
commit
40ca9e6ed2
71 changed files with 232 additions and 1676 deletions
|
|
@ -1,16 +1,13 @@
|
|||
"use client";
|
||||
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useSetAtom } from "jotai";
|
||||
import { ExternalLink, FileText } from "lucide-react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { FileText } from "lucide-react";
|
||||
import type { FC } from "react";
|
||||
import { useState } from "react";
|
||||
import { openCitationPanelAtom } from "@/atoms/citation/citation-panel.atom";
|
||||
import { useCitationMetadata } from "@/components/assistant-ui/citation-metadata-context";
|
||||
import { CitationPanelContent } from "@/components/citation-panel/citation-panel";
|
||||
import { Citation } from "@/components/tool-ui/citation";
|
||||
import { CitationHoverPopover } from "@/components/tool-ui/citation/citation-hover-popover";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Drawer,
|
||||
|
|
@ -19,21 +16,8 @@ import {
|
|||
DrawerHeader,
|
||||
DrawerTitle,
|
||||
} from "@/components/ui/drawer";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { useMediaQuery } from "@/hooks/use-media-query";
|
||||
import { documentsApiService } from "@/lib/apis/documents-api.service";
|
||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||
|
||||
// Lazily load MarkdownViewer here to break the static import cycle:
|
||||
// `markdown-viewer.tsx` → `citation-renderer.tsx` → `inline-citation.tsx`
|
||||
// would otherwise pull `markdown-viewer.tsx` back in at module-init time.
|
||||
// Only `SurfsenseDocCitation` (popover body) ever renders this viewer, so
|
||||
// the lazy boundary is invisible to most call paths.
|
||||
const MarkdownViewer = dynamic(
|
||||
() => import("@/components/markdown-viewer").then((m) => m.MarkdownViewer),
|
||||
{ ssr: false, loading: () => <Spinner size="xs" /> }
|
||||
);
|
||||
|
||||
interface InlineCitationProps {
|
||||
chunkId: number;
|
||||
|
|
@ -41,9 +25,7 @@ interface InlineCitationProps {
|
|||
}
|
||||
|
||||
/**
|
||||
* Inline citation badge for knowledge-base chunks (numeric chunk IDs) and
|
||||
* Surfsense documentation chunks (`isDocsChunk`). Negative chunk IDs render as
|
||||
* a static "doc" pill (anonymous/synthetic uploads).
|
||||
* Inline citation badge for knowledge-base chunks (numeric chunk IDs).
|
||||
*
|
||||
* Numeric KB chunks: clicking opens the citation panel in the right
|
||||
* sidebar (alongside the chat — does not replace it). The panel shows
|
||||
|
|
@ -51,12 +33,13 @@ interface InlineCitationProps {
|
|||
* `chunk_window`), with the cited one highlighted and an option to
|
||||
* expand the window or jump into the full document via the editor panel.
|
||||
*
|
||||
* Surfsense docs chunks: rendered as a hover-controlled shadcn Popover that
|
||||
* lazily fetches and previews the cited chunk inline, since those docs aren't
|
||||
* indexed into the user's search space and have no tab to open.
|
||||
* Negative chunk IDs and legacy SurfSense-docs chunks (`isDocsChunk`) render
|
||||
* as a static, non-interactive "doc" pill. The SurfSense product-docs feature
|
||||
* was removed, so those markers are inert (no fetch, no preview) — they only
|
||||
* survive in old persisted messages.
|
||||
*/
|
||||
export const InlineCitation: FC<InlineCitationProps> = ({ chunkId, isDocsChunk = false }) => {
|
||||
if (chunkId < 0) {
|
||||
if (chunkId < 0 || isDocsChunk) {
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
|
|
@ -68,15 +51,11 @@ export const InlineCitation: FC<InlineCitationProps> = ({ chunkId, isDocsChunk =
|
|||
doc
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Uploaded document</TooltipContent>
|
||||
<TooltipContent>{isDocsChunk ? "Documentation reference" : "Uploaded document"}</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
if (isDocsChunk) {
|
||||
return <SurfsenseDocCitation chunkId={chunkId} />;
|
||||
}
|
||||
|
||||
return <NumericChunkCitation chunkId={chunkId} />;
|
||||
};
|
||||
|
||||
|
|
@ -127,128 +106,6 @@ const NumericChunkCitation: FC<{ chunkId: number }> = ({ chunkId }) => {
|
|||
);
|
||||
};
|
||||
|
||||
const SurfsenseDocCitation: FC<{ chunkId: number }> = ({ chunkId }) => {
|
||||
const isTouchLike = useMediaQuery("(hover: none), (pointer: coarse)");
|
||||
const [mobilePreviewOpen, setMobilePreviewOpen] = useState(false);
|
||||
const docQuery = useSurfsenseDocPreviewQuery(chunkId, mobilePreviewOpen);
|
||||
|
||||
const handleMobileClick = () => {
|
||||
setMobilePreviewOpen(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<CitationHoverPopover
|
||||
id={`doc-${chunkId}`}
|
||||
contentClassName="w-96 max-w-[calc(100vw-2rem)] p-0"
|
||||
align="start"
|
||||
trigger={(hoverProps) => (
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size={null}
|
||||
onClick={isTouchLike ? handleMobileClick : undefined}
|
||||
className="ml-0.5 inline-flex h-5 min-w-5 items-center justify-center gap-0.5 rounded-md bg-popover px-1.5 text-[11px] font-medium text-popover-foreground/80 align-baseline"
|
||||
aria-label={`Show Surfsense documentation chunk ${chunkId}`}
|
||||
title="Surfsense documentation"
|
||||
{...hoverProps}
|
||||
>
|
||||
<FileText className="size-3" />
|
||||
doc
|
||||
</Button>
|
||||
)}
|
||||
>
|
||||
<SurfsenseDocPreview chunkId={chunkId} />
|
||||
</CitationHoverPopover>
|
||||
<Drawer
|
||||
open={mobilePreviewOpen}
|
||||
onOpenChange={setMobilePreviewOpen}
|
||||
shouldScaleBackground={false}
|
||||
>
|
||||
<DrawerContent className="max-h-[85vh] z-80" overlayClassName="z-80">
|
||||
<DrawerHandle />
|
||||
<DrawerHeader className="pb-0">
|
||||
<DrawerTitle>Surfsense documentation</DrawerTitle>
|
||||
</DrawerHeader>
|
||||
<SurfsenseDocPreviewContent
|
||||
chunkId={chunkId}
|
||||
query={docQuery}
|
||||
contentClassName="max-h-[60vh]"
|
||||
/>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
function useSurfsenseDocPreviewQuery(chunkId: number, enabled = true) {
|
||||
return useQuery({
|
||||
queryKey: cacheKeys.documents.byChunk(`doc-${chunkId}`),
|
||||
queryFn: () => documentsApiService.getSurfsenseDocByChunk(chunkId),
|
||||
staleTime: 5 * 60 * 1000,
|
||||
enabled,
|
||||
});
|
||||
}
|
||||
|
||||
type SurfsenseDocPreviewQuery = ReturnType<typeof useSurfsenseDocPreviewQuery>;
|
||||
|
||||
const SurfsenseDocPreview: FC<{ chunkId: number }> = ({ chunkId }) => {
|
||||
const query = useSurfsenseDocPreviewQuery(chunkId);
|
||||
|
||||
return <SurfsenseDocPreviewContent chunkId={chunkId} query={query} />;
|
||||
};
|
||||
|
||||
const SurfsenseDocPreviewContent: FC<{
|
||||
chunkId: number;
|
||||
query: SurfsenseDocPreviewQuery;
|
||||
contentClassName?: string;
|
||||
}> = ({ chunkId, query, contentClassName = "max-h-72" }) => {
|
||||
const { data, isLoading, error } = query;
|
||||
|
||||
const citedChunk = data?.chunks.find((c) => c.id === chunkId) ?? data?.chunks[0];
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center justify-between gap-2 border-b px-3 py-2">
|
||||
<div className="min-w-0">
|
||||
<p className="truncate text-sm font-medium">{data?.title ?? "Surfsense documentation"}</p>
|
||||
<p className="text-[11px] text-muted-foreground">Chunk #{chunkId}</p>
|
||||
</div>
|
||||
{data?.public_url && (
|
||||
<a
|
||||
href={data.public_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex shrink-0 items-center gap-1 rounded-md px-2 py-1 text-[11px] font-medium text-primary hover:bg-primary/10"
|
||||
>
|
||||
<ExternalLink className="size-3" />
|
||||
Open
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
<div className={`${contentClassName} overflow-auto px-3 py-2 text-sm`}>
|
||||
{isLoading && (
|
||||
<div className="flex items-center gap-2 py-4 text-muted-foreground">
|
||||
<Spinner size="xs" />
|
||||
<span className="text-xs">Loading…</span>
|
||||
</div>
|
||||
)}
|
||||
{error && (
|
||||
<p className="py-4 text-xs text-destructive">
|
||||
{error instanceof Error ? error.message : "Failed to load chunk"}
|
||||
</p>
|
||||
)}
|
||||
{!isLoading && !error && citedChunk?.content && (
|
||||
<MarkdownViewer content={citedChunk.content} maxLength={1500} enableCitations />
|
||||
)}
|
||||
{!isLoading && !error && !citedChunk?.content && (
|
||||
<p className="py-4 text-xs text-muted-foreground">No content available.</p>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
import { tryGetHostname } from "@/lib/url";
|
||||
|
||||
interface UrlCitationProps {
|
||||
|
|
|
|||
|
|
@ -1593,7 +1593,7 @@ interface ToolGroup {
|
|||
const TOOL_GROUPS: ToolGroup[] = [
|
||||
{
|
||||
label: "Research",
|
||||
tools: ["search_surfsense_docs", "scrape_webpage"],
|
||||
tools: ["scrape_webpage"],
|
||||
},
|
||||
{
|
||||
label: "Generate",
|
||||
|
|
|
|||
|
|
@ -90,7 +90,6 @@ const DesktopLocalTabContent = dynamic(
|
|||
);
|
||||
|
||||
const NON_DELETABLE_DOCUMENT_TYPES: readonly string[] = [
|
||||
"SURFSENSE_DOCS",
|
||||
"USER_MEMORY",
|
||||
"TEAM_MEMORY",
|
||||
];
|
||||
|
|
|
|||
|
|
@ -3,14 +3,7 @@
|
|||
import { useQuery as useZeroQuery } from "@rocicorp/zero/react";
|
||||
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
||||
import { useAtomValue } from "jotai";
|
||||
import {
|
||||
BookOpen,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
Files,
|
||||
Folder as FolderIcon,
|
||||
Unplug,
|
||||
} from "lucide-react";
|
||||
import { ChevronLeft, ChevronRight, Files, Folder as FolderIcon, Unplug } from "lucide-react";
|
||||
import {
|
||||
Fragment,
|
||||
forwardRef,
|
||||
|
|
@ -57,13 +50,6 @@ interface DocumentMentionPickerProps {
|
|||
onDone: () => void;
|
||||
initialSelectedDocuments?: MentionedDocumentInfo[];
|
||||
externalSearch?: string;
|
||||
/**
|
||||
* Whether to surface the "SurfSense Docs" (product documentation) branch
|
||||
* and include those docs in search results. Defaults to ``true`` so the
|
||||
* chat composer is unchanged; callers like the automation task input pass
|
||||
* ``false`` to reference only the user's own knowledge base + connectors.
|
||||
*/
|
||||
includeSurfsenseDocs?: boolean;
|
||||
}
|
||||
|
||||
const PAGE_SIZE = 20;
|
||||
|
|
@ -74,7 +60,6 @@ const RECENTS_STORAGE_PREFIX = "surfsense:composer-mention-recents:v1:";
|
|||
|
||||
type BrowseView =
|
||||
| { kind: "root" }
|
||||
| { kind: "surfsense-docs" }
|
||||
| { kind: "files-folders" }
|
||||
| { kind: "connectors" }
|
||||
| { kind: "connector-type"; connectorType: string; title: string };
|
||||
|
|
@ -241,7 +226,6 @@ export const DocumentMentionPicker = forwardRef<
|
|||
onDone,
|
||||
initialSelectedDocuments = [],
|
||||
externalSearch = "",
|
||||
includeSurfsenseDocs = true,
|
||||
},
|
||||
ref
|
||||
) {
|
||||
|
|
@ -298,15 +282,6 @@ export const DocumentMentionPicker = forwardRef<
|
|||
[searchSpaceId, debouncedSearch, isSearchValid]
|
||||
);
|
||||
|
||||
const surfsenseDocsQueryParams = useMemo(() => {
|
||||
const params: { page: number; page_size: number; title?: string } = {
|
||||
page: 0,
|
||||
page_size: PAGE_SIZE,
|
||||
};
|
||||
if (isSearchValid) params.title = debouncedSearch.trim();
|
||||
return params;
|
||||
}, [debouncedSearch, isSearchValid]);
|
||||
|
||||
const { data: titleSearchResults, isLoading: isTitleSearchLoading } = useQuery({
|
||||
queryKey: ["document-titles", titleSearchParams],
|
||||
queryFn: ({ signal }) =>
|
||||
|
|
@ -316,15 +291,6 @@ export const DocumentMentionPicker = forwardRef<
|
|||
placeholderData: keepPreviousData,
|
||||
});
|
||||
|
||||
const { data: surfsenseDocs, isLoading: isSurfsenseDocsLoading } = useQuery({
|
||||
queryKey: ["surfsense-docs-mention", debouncedSearch, isSearchValid],
|
||||
queryFn: ({ signal }) =>
|
||||
documentsApiService.getSurfsenseDocs({ queryParams: surfsenseDocsQueryParams }, signal),
|
||||
staleTime: 3 * 60 * 1000,
|
||||
enabled: includeSurfsenseDocs && (!hasSearch || isSearchValid),
|
||||
placeholderData: keepPreviousData,
|
||||
});
|
||||
|
||||
const filterBySearchTerm = useCallback(
|
||||
(docs: Pick<Document, "id" | "title" | "document_type">[]) => {
|
||||
if (!isSearchValid) return docs;
|
||||
|
|
@ -338,23 +304,13 @@ export const DocumentMentionPicker = forwardRef<
|
|||
if (currentPage !== 0) return;
|
||||
const combinedDocs: Pick<Document, "id" | "title" | "document_type">[] = [];
|
||||
|
||||
if (includeSurfsenseDocs && surfsenseDocs?.items) {
|
||||
for (const doc of surfsenseDocs.items) {
|
||||
combinedDocs.push({
|
||||
id: doc.id,
|
||||
title: doc.title,
|
||||
document_type: "SURFSENSE_DOCS",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (titleSearchResults?.items) {
|
||||
combinedDocs.push(...titleSearchResults.items);
|
||||
setHasMore(titleSearchResults.has_more);
|
||||
}
|
||||
|
||||
setAccumulatedDocuments(filterBySearchTerm(combinedDocs));
|
||||
}, [titleSearchResults, surfsenseDocs, currentPage, filterBySearchTerm, includeSurfsenseDocs]);
|
||||
}, [titleSearchResults, currentPage, filterBySearchTerm]);
|
||||
|
||||
const loadNextPage = useCallback(async () => {
|
||||
if (isLoadingMore || !hasMore) return;
|
||||
|
|
@ -391,14 +347,6 @@ export const DocumentMentionPicker = forwardRef<
|
|||
return accumulatedDocuments.filter((doc) => doc.title.toLowerCase().includes(searchLower));
|
||||
}, [accumulatedDocuments, deferredSearch, isSingleCharSearch]);
|
||||
|
||||
const surfsenseDocsList = useMemo(
|
||||
() => actualDocuments.filter((doc) => doc.document_type === "SURFSENSE_DOCS"),
|
||||
[actualDocuments]
|
||||
);
|
||||
const userDocsList = useMemo(
|
||||
() => actualDocuments.filter((doc) => doc.document_type !== "SURFSENSE_DOCS"),
|
||||
[actualDocuments]
|
||||
);
|
||||
const folderMentions = useMemo(() => {
|
||||
const all = (zeroFolders ?? []).map((f) => makeFolderMention({ id: f.id, title: f.name }));
|
||||
if (!hasSearch) return all;
|
||||
|
|
@ -463,7 +411,6 @@ export const DocumentMentionPicker = forwardRef<
|
|||
() => new Set(initialSelectedDocuments.map((d) => getMentionDocKey(d))),
|
||||
[initialSelectedDocuments]
|
||||
);
|
||||
const showSurfsenseDocsRoot = includeSurfsenseDocs && surfsenseDocsList.length > 0;
|
||||
|
||||
const selectMention = useCallback(
|
||||
(mention: MentionedDocumentInfo) => {
|
||||
|
|
@ -487,16 +434,6 @@ export const DocumentMentionPicker = forwardRef<
|
|||
|
||||
const rootNodes = useMemo<ComposerSuggestionNode<ResourceNodeValue>[]>(() => {
|
||||
const nodes: ComposerSuggestionNode<ResourceNodeValue>[] = [...recentRootNodes];
|
||||
if (showSurfsenseDocsRoot) {
|
||||
nodes.push({
|
||||
id: "surfsense-docs",
|
||||
label: "SurfSense Docs",
|
||||
subtitle: "Browse product documentation",
|
||||
icon: <BookOpen className="size-4" />,
|
||||
type: "branch",
|
||||
value: { kind: "view", view: { kind: "surfsense-docs" } },
|
||||
});
|
||||
}
|
||||
nodes.push(
|
||||
{
|
||||
id: "files-folders",
|
||||
|
|
@ -519,7 +456,7 @@ export const DocumentMentionPicker = forwardRef<
|
|||
}
|
||||
);
|
||||
return nodes;
|
||||
}, [activeConnectors.length, recentRootNodes, showSurfsenseDocsRoot]);
|
||||
}, [activeConnectors.length, recentRootNodes]);
|
||||
|
||||
const searchNodes = useMemo<ComposerSuggestionNode<ResourceNodeValue>[]>(() => {
|
||||
const searchLower = (isSingleCharSearch ? deferredSearch : debouncedSearch)
|
||||
|
|
@ -582,19 +519,6 @@ export const DocumentMentionPicker = forwardRef<
|
|||
|
||||
const browseNodes = useMemo<ComposerSuggestionNode<ResourceNodeValue>[]>(() => {
|
||||
if (view.kind === "root") return rootNodes;
|
||||
if (view.kind === "surfsense-docs") {
|
||||
return surfsenseDocsList.map((doc) => {
|
||||
const mention = makeDocMention(doc);
|
||||
return {
|
||||
id: getMentionDocKey(mention),
|
||||
label: doc.title,
|
||||
icon: getConnectorIcon(doc.document_type, "size-4"),
|
||||
type: "item" as const,
|
||||
disabled: selectedKeys.has(getMentionDocKey(mention)),
|
||||
value: { kind: "mention" as const, mention },
|
||||
};
|
||||
});
|
||||
}
|
||||
if (view.kind === "files-folders") {
|
||||
const folders = folderMentions.map((mention) => ({
|
||||
id: getMentionDocKey(mention),
|
||||
|
|
@ -605,7 +529,7 @@ export const DocumentMentionPicker = forwardRef<
|
|||
disabled: selectedKeys.has(getMentionDocKey(mention)),
|
||||
value: { kind: "mention" as const, mention },
|
||||
}));
|
||||
const docs = userDocsList.map((doc) => {
|
||||
const docs = actualDocuments.map((doc) => {
|
||||
const mention = makeDocMention(doc);
|
||||
return {
|
||||
id: getMentionDocKey(mention),
|
||||
|
|
@ -652,13 +576,12 @@ export const DocumentMentionPicker = forwardRef<
|
|||
};
|
||||
});
|
||||
}, [
|
||||
actualDocuments,
|
||||
activeConnectors,
|
||||
connectorTypeEntries,
|
||||
folderMentions,
|
||||
rootNodes,
|
||||
selectedKeys,
|
||||
surfsenseDocsList,
|
||||
userDocsList,
|
||||
view,
|
||||
]);
|
||||
|
||||
|
|
@ -708,27 +631,23 @@ export const DocumentMentionPicker = forwardRef<
|
|||
|
||||
const isRootBrowseView = !hasSearch && view.kind === "root";
|
||||
const isVisibleViewLoading = hasSearch
|
||||
? isTitleSearchLoading || isSurfsenseDocsLoading || isConnectorsLoading
|
||||
: view.kind === "surfsense-docs"
|
||||
? isSurfsenseDocsLoading
|
||||
: view.kind === "files-folders"
|
||||
? isTitleSearchLoading
|
||||
: view.kind === "connectors" || view.kind === "connector-type"
|
||||
? isConnectorsLoading
|
||||
: false;
|
||||
? isTitleSearchLoading || isConnectorsLoading
|
||||
: view.kind === "files-folders"
|
||||
? isTitleSearchLoading
|
||||
: view.kind === "connectors" || view.kind === "connector-type"
|
||||
? isConnectorsLoading
|
||||
: false;
|
||||
const actualLoading =
|
||||
isVisibleViewLoading && !isSingleCharSearch && visibleNodes.length === 0 && !isRootBrowseView;
|
||||
|
||||
const title =
|
||||
hasSearch || view.kind === "root"
|
||||
? null
|
||||
: view.kind === "surfsense-docs"
|
||||
? "SurfSense Docs"
|
||||
: view.kind === "files-folders"
|
||||
? "Files & Folders"
|
||||
: view.kind === "connectors"
|
||||
? "Connectors"
|
||||
: view.title;
|
||||
: view.kind === "files-folders"
|
||||
? "Files & Folders"
|
||||
: view.kind === "connectors"
|
||||
? "Connectors"
|
||||
: view.title;
|
||||
|
||||
return (
|
||||
<ComposerSuggestionList
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue