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:
DESKTOP-RTLN3BA\$punk 2026-05-28 22:35:14 -07:00
parent 9b9e6828c7
commit 40ca9e6ed2
71 changed files with 232 additions and 1676 deletions

View file

@ -89,10 +89,10 @@ function removeFirstToken(text: string, token: string): string {
/**
* Task input that reuses the chat ``@`` mention experience -- the same
* ``InlineMentionEditor`` + ``DocumentMentionPicker`` as the composer, minus
* SurfSense product docs. The editor is the source of truth while mounted;
* ``onChange`` reports both the plain text (chips rendered as ``@Title``) and
* the structured mention list so the builder can persist IDs for the run.
* ``InlineMentionEditor`` + ``DocumentMentionPicker`` as the composer. The
* editor is the source of truth while mounted; ``onChange`` reports both the
* plain text (chips rendered as ``@Title``) and the structured mention list
* so the builder can persist IDs for the run.
*/
export function MentionTaskInput({
searchSpaceId,
@ -233,7 +233,6 @@ export function MentionTaskInput({
<DocumentMentionPicker
ref={pickerRef}
searchSpaceId={searchSpaceId}
includeSurfsenseDocs={false}
onSelectionChange={handleSelection}
onDone={closePopover}
initialSelectedDocuments={mentions}

View file

@ -740,15 +740,6 @@ export default function NewChatPage() {
queryFn: () => documentsApiService.searchDocumentTitles({ queryParams: prefetchParams }),
staleTime: 60 * 1000,
});
queryClient.prefetchQuery({
queryKey: ["surfsense-docs-mention", "", false],
queryFn: () =>
documentsApiService.getSurfsenseDocs({
queryParams: { page: 0, page_size: 20 },
}),
staleTime: 3 * 60 * 1000,
});
}, [searchSpaceId, queryClient]);
// Handle scroll to comment from URL query params (e.g., from inbox item click)
@ -949,7 +940,6 @@ export default function NewChatPage() {
trackChatMessageSent(searchSpaceId, currentThreadId, {
hasAttachments: userImages.length > 0,
hasMentionedDocuments:
mentionedDocumentIds.surfsense_doc_ids.length > 0 ||
mentionedDocumentIds.document_ids.length > 0 ||
mentionedDocumentIds.folder_ids.length > 0 ||
mentionedDocumentIds.connector_ids.length > 0,
@ -1027,12 +1017,11 @@ 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;
const hasConnectorIds = mentionedDocumentIds.connector_ids.length > 0;
// Clear mentioned documents after capturing them
if (hasDocumentIds || hasSurfsenseDocIds || hasFolderIds || hasConnectorIds) {
if (hasDocumentIds || hasFolderIds || hasConnectorIds) {
setMentionedDocuments([]);
}
@ -1054,9 +1043,6 @@ export default function NewChatPage() {
mentioned_document_ids: hasDocumentIds
? mentionedDocumentIds.document_ids
: undefined,
mentioned_surfsense_doc_ids: hasSurfsenseDocIds
? mentionedDocumentIds.surfsense_doc_ids
: undefined,
mentioned_folder_ids: hasFolderIds ? mentionedDocumentIds.folder_ids : undefined,
mentioned_connector_ids: hasConnectorIds
? mentionedDocumentIds.connector_ids
@ -1947,18 +1933,14 @@ 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);
// Partition the source mentions back into 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 regenerateDocIds = sourceMentionedDocs
.filter((d) => d.kind === "doc" && d.document_type !== "SURFSENSE_DOCS")
.filter((d) => d.kind === "doc")
.map((d) => d.id);
const regenerateFolderIds = sourceMentionedDocs
.filter((d) => d.kind === "folder")
@ -1973,8 +1955,6 @@ export default function NewChatPage() {
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,
mentioned_connector_ids:
regenerateConnectors.length > 0 ? regenerateConnectors.map((d) => d.id) : undefined,

View file

@ -102,10 +102,7 @@ export const mentionedDocumentIdsAtom = atom((get) => {
const folders = deduped.filter((m) => m.kind === "folder");
const connectors = deduped.filter((m) => m.kind === "connector");
return {
surfsense_doc_ids: docs
.filter((doc) => doc.document_type === "SURFSENSE_DOCS")
.map((doc) => doc.id),
document_ids: docs.filter((doc) => doc.document_type !== "SURFSENSE_DOCS").map((doc) => doc.id),
document_ids: docs.map((doc) => doc.id),
folder_ids: folders.map((f) => f.id),
connector_ids: connectors.map((c) => c.id),
connectors: connectors.map((c) => ({

View file

@ -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 {

View file

@ -1593,7 +1593,7 @@ interface ToolGroup {
const TOOL_GROUPS: ToolGroup[] = [
{
label: "Research",
tools: ["search_surfsense_docs", "scrape_webpage"],
tools: ["scrape_webpage"],
},
{
label: "Generate",

View file

@ -90,7 +90,6 @@ const DesktopLocalTabContent = dynamic(
);
const NON_DELETABLE_DOCUMENT_TYPES: readonly string[] = [
"SURFSENSE_DOCS",
"USER_MEMORY",
"TEAM_MEMORY",
];

View file

@ -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

View file

@ -1,6 +1,5 @@
import { IconUsersGroup } from "@tabler/icons-react";
import {
BookOpen,
Brain,
File,
FileText,
@ -119,8 +118,6 @@ export const getConnectorIcon = (connectorType: EnumConnectorName | string, clas
return <FileText {...iconProps} />;
case "EXTENSION":
return <Webhook {...iconProps} />;
case "SURFSENSE_DOCS":
return <BookOpen {...iconProps} />;
case "USER_MEMORY":
case "TEAM_MEMORY":
return <Brain {...iconProps} />;

View file

@ -1,5 +1,4 @@
import {
BookOpen,
Brain,
Calendar,
FileEdit,
@ -47,7 +46,6 @@ const TOOL_ICONS: Record<string, LucideIcon> = {
// Web / search
scrape_webpage: ScanLine,
web_search: Globe,
search_surfsense_docs: BookOpen,
// Automations
create_automation: Workflow,
// Memory
@ -152,7 +150,6 @@ const TOOL_DISPLAY_NAMES: Record<string, string> = {
// Web / search
scrape_webpage: "Read webpage",
web_search: "Search the web",
search_surfsense_docs: "Search knowledge base",
// Automations
create_automation: "Create automation",
// Memory

View file

@ -27,7 +27,6 @@ export const documentTypeEnum = z.enum([
"CIRCLEBACK",
"OBSIDIAN_CONNECTOR",
"LOCAL_FOLDER_FILE",
"SURFSENSE_DOCS",
"NOTE",
"USER_MEMORY",
"TEAM_MEMORY",
@ -77,27 +76,6 @@ export const documentWithChunks = document.extend({
chunk_start_index: z.number().optional().default(0),
});
/**
* Surfsense documentation schemas
* Follows the same pattern as document/documentWithChunks
*/
export const surfsenseDocsChunk = z.object({
id: z.number(),
content: z.string(),
});
export const surfsenseDocsDocument = z.object({
id: z.number(),
title: z.string(),
source: z.string(),
public_url: z.string(),
content: z.string(),
});
export const surfsenseDocsDocumentWithChunks = surfsenseDocsDocument.extend({
chunks: z.array(surfsenseDocsChunk),
});
/**
* Get documents
*/
@ -284,32 +262,6 @@ export const getDocumentChunksResponse = z.object({
has_more: z.boolean(),
});
/**
* Get Surfsense docs by chunk
*/
export const getSurfsenseDocsByChunkRequest = z.object({
chunk_id: z.number(),
});
export const getSurfsenseDocsByChunkResponse = surfsenseDocsDocumentWithChunks;
/**
* List Surfsense docs
*/
export const getSurfsenseDocsRequest = z.object({
queryParams: paginationQueryParams.extend({
title: z.string().optional(),
}),
});
export const getSurfsenseDocsResponse = z.object({
items: z.array(surfsenseDocsDocument),
total: z.number(),
page: z.number(),
page_size: z.number(),
has_more: z.boolean(),
});
/**
* Update document
*/
@ -358,13 +310,6 @@ export type DeleteDocumentResponse = z.infer<typeof deleteDocumentResponse>;
export type DocumentTypeEnum = z.infer<typeof documentTypeEnum>;
export type DocumentSortBy = z.infer<typeof documentSortByEnum>;
export type SortOrder = z.infer<typeof sortOrderEnum>;
export type SurfsenseDocsChunk = z.infer<typeof surfsenseDocsChunk>;
export type SurfsenseDocsDocument = z.infer<typeof surfsenseDocsDocument>;
export type SurfsenseDocsDocumentWithChunks = z.infer<typeof surfsenseDocsDocumentWithChunks>;
export type GetSurfsenseDocsByChunkRequest = z.infer<typeof getSurfsenseDocsByChunkRequest>;
export type GetSurfsenseDocsByChunkResponse = z.infer<typeof getSurfsenseDocsByChunkResponse>;
export type GetSurfsenseDocsRequest = z.infer<typeof getSurfsenseDocsRequest>;
export type GetSurfsenseDocsResponse = z.infer<typeof getSurfsenseDocsResponse>;
export type GetDocumentChunksRequest = z.infer<typeof getDocumentChunksRequest>;
export type GetDocumentChunksResponse = z.infer<typeof getDocumentChunksResponse>;
export type ChunkRead = z.infer<typeof chunkRead>;

View file

@ -12,7 +12,6 @@ import {
type GetDocumentsRequest,
type GetDocumentsStatusRequest,
type GetDocumentTypeCountsRequest,
type GetSurfsenseDocsRequest,
getDocumentByChunkRequest,
getDocumentByChunkResponse,
getDocumentChunksRequest,
@ -25,9 +24,6 @@ import {
getDocumentsStatusResponse,
getDocumentTypeCountsRequest,
getDocumentTypeCountsResponse,
getSurfsenseDocsByChunkResponse,
getSurfsenseDocsRequest,
getSurfsenseDocsResponse,
type SearchDocumentsRequest,
type SearchDocumentTitlesRequest,
searchDocumentsRequest,
@ -363,48 +359,6 @@ class DocumentsApiService {
);
};
/**
* Get Surfsense documentation by chunk ID
* Used for resolving [citation:doc-XXX] citations
*/
getSurfsenseDocByChunk = async (chunkId: number) => {
return baseApiService.get(
`/api/v1/surfsense-docs/by-chunk/${chunkId}`,
getSurfsenseDocsByChunkResponse
);
};
/**
* List all Surfsense documentation documents
* @param request - The request with query params
* @param signal - Optional AbortSignal for request cancellation
*/
getSurfsenseDocs = async (request: GetSurfsenseDocsRequest, signal?: AbortSignal) => {
const parsedRequest = getSurfsenseDocsRequest.safeParse(request);
if (!parsedRequest.success) {
console.error("Invalid request:", parsedRequest.error);
const errorMessage = parsedRequest.error.issues.map((issue) => issue.message).join(", ");
throw new ValidationError(`Invalid request: ${errorMessage}`);
}
// Transform query params to be string values
const transformedQueryParams = parsedRequest.data.queryParams
? Object.fromEntries(
Object.entries(parsedRequest.data.queryParams).map(([k, v]) => [k, String(v)])
)
: undefined;
const queryParams = transformedQueryParams
? new URLSearchParams(transformedQueryParams).toString()
: "";
const url = `/api/v1/surfsense-docs?${queryParams}`;
return baseApiService.get(url, getSurfsenseDocsResponse, { signal });
};
/**
* Update a document
*/

View file

@ -221,7 +221,6 @@ export interface RegenerateParams {
content: string;
}>;
mentionedDocumentIds?: number[];
mentionedSurfsenseDocIds?: number[];
}
/**

View file

@ -25,7 +25,6 @@ export function getDocumentTypeLabel(type: string): string {
CIRCLEBACK: "Circleback",
OBSIDIAN_CONNECTOR: "Obsidian",
LOCAL_FOLDER_FILE: "Local Folder",
SURFSENSE_DOCS: "SurfSense Docs",
NOTE: "Note",
COMPOSIO_GOOGLE_DRIVE_CONNECTOR: "Composio Google Drive",
COMPOSIO_GMAIL_CONNECTOR: "Composio Gmail",

View file

@ -30,7 +30,6 @@ export const cacheKeys = {
withQueryParams: (queries: GetDocumentsRequest["queryParams"]) =>
["documents-with-queries", ...stableEntries(queries)] as const,
document: (documentId: string) => ["document", documentId] as const,
byChunk: (chunkId: string) => ["documents", "by-chunk", chunkId] as const,
},
logs: {
list: (searchSpaceId?: number | string) => ["logs", "list", searchSpaceId] as const,

BIN
surfsense_web/tsc_out.txt Normal file

Binary file not shown.