diff --git a/surfsense_backend/alembic/versions/120_add_vision_llm_configs_table.py b/surfsense_backend/alembic/versions/120_add_vision_llm_configs_table.py index c0c915388..62faf5c4f 100644 --- a/surfsense_backend/alembic/versions/120_add_vision_llm_configs_table.py +++ b/surfsense_backend/alembic/versions/120_add_vision_llm_configs_table.py @@ -79,7 +79,9 @@ def upgrade() -> None: sa.Column("description", sa.String(500), nullable=True), sa.Column( "provider", - PG_ENUM(*VISION_PROVIDER_VALUES, name="visionprovider", create_type=False), + PG_ENUM( + *VISION_PROVIDER_VALUES, name="visionprovider", create_type=False + ), nullable=False, ), sa.Column("custom_provider", sa.String(100), nullable=True), @@ -100,9 +102,7 @@ def upgrade() -> None: sa.ForeignKeyConstraint( ["search_space_id"], ["searchspaces.id"], ondelete="CASCADE" ), - sa.ForeignKeyConstraint( - ["user_id"], ["user.id"], ondelete="CASCADE" - ), + sa.ForeignKeyConstraint(["user_id"], ["user.id"], ondelete="CASCADE"), ) op.execute( "CREATE INDEX IF NOT EXISTS ix_vision_llm_configs_name " @@ -117,12 +117,19 @@ def upgrade() -> None: existing_columns = [ col["name"] for col in sa.inspect(connection).get_columns("searchspaces") ] - if "vision_llm_id" in existing_columns and "vision_llm_config_id" not in existing_columns: - op.alter_column("searchspaces", "vision_llm_id", new_column_name="vision_llm_config_id") + if ( + "vision_llm_id" in existing_columns + and "vision_llm_config_id" not in existing_columns + ): + op.alter_column( + "searchspaces", "vision_llm_id", new_column_name="vision_llm_config_id" + ) elif "vision_llm_config_id" not in existing_columns: op.add_column( "searchspaces", - sa.Column("vision_llm_config_id", sa.Integer(), nullable=True, server_default="0"), + sa.Column( + "vision_llm_config_id", sa.Integer(), nullable=True, server_default="0" + ), ) # 4. Add vision config permissions to existing system roles @@ -181,7 +188,9 @@ def downgrade() -> None: col["name"] for col in sa.inspect(connection).get_columns("searchspaces") ] if "vision_llm_config_id" in existing_columns: - op.alter_column("searchspaces", "vision_llm_config_id", new_column_name="vision_llm_id") + op.alter_column( + "searchspaces", "vision_llm_config_id", new_column_name="vision_llm_id" + ) # Drop table and enum op.execute("DROP INDEX IF EXISTS ix_vision_llm_configs_search_space_id") diff --git a/surfsense_backend/app/agents/autocomplete/autocomplete_agent.py b/surfsense_backend/app/agents/autocomplete/autocomplete_agent.py index 77c0af5bb..2d8f05fd3 100644 --- a/surfsense_backend/app/agents/autocomplete/autocomplete_agent.py +++ b/surfsense_backend/app/agents/autocomplete/autocomplete_agent.py @@ -452,9 +452,7 @@ async def stream_autocomplete_agent( raw_text = "".join(text_buffer) suggestions = _parse_suggestions(raw_text) - yield streaming_service.format_data( - "suggestions", {"options": suggestions} - ) + yield streaming_service.format_data("suggestions", {"options": suggestions}) yield streaming_service.format_finish() yield streaming_service.format_done() diff --git a/surfsense_backend/app/db.py b/surfsense_backend/app/db.py index 4689313f7..77a001a0d 100644 --- a/surfsense_backend/app/db.py +++ b/surfsense_backend/app/db.py @@ -1310,9 +1310,7 @@ class VisionLLMConfig(BaseModel, TimestampMixin): search_space_id = Column( Integer, ForeignKey("searchspaces.id", ondelete="CASCADE"), nullable=False ) - search_space = relationship( - "SearchSpace", back_populates="vision_llm_configs" - ) + search_space = relationship("SearchSpace", back_populates="vision_llm_configs") user_id = Column( UUID(as_uuid=True), ForeignKey("user.id", ondelete="CASCADE"), nullable=False diff --git a/surfsense_backend/app/etl_pipeline/etl_pipeline_service.py b/surfsense_backend/app/etl_pipeline/etl_pipeline_service.py index 167e43765..fbd2e4e73 100644 --- a/surfsense_backend/app/etl_pipeline/etl_pipeline_service.py +++ b/surfsense_backend/app/etl_pipeline/etl_pipeline_service.py @@ -122,6 +122,4 @@ class EtlPipelineService: from app.etl_pipeline.parsers.llamacloud import parse_with_llamacloud - return await parse_with_llamacloud( - request.file_path, request.estimated_pages - ) + return await parse_with_llamacloud(request.file_path, request.estimated_pages) diff --git a/surfsense_backend/app/routes/vision_llm_routes.py b/surfsense_backend/app/routes/vision_llm_routes.py index eddd5e367..315c7c9fe 100644 --- a/surfsense_backend/app/routes/vision_llm_routes.py +++ b/surfsense_backend/app/routes/vision_llm_routes.py @@ -180,9 +180,7 @@ async def list_vision_llm_configs( ) from e -@router.get( - "/vision-llm-configs/{config_id}", response_model=VisionLLMConfigRead -) +@router.get("/vision-llm-configs/{config_id}", response_model=VisionLLMConfigRead) async def get_vision_llm_config( config_id: int, session: AsyncSession = Depends(get_async_session), @@ -214,9 +212,7 @@ async def get_vision_llm_config( ) from e -@router.put( - "/vision-llm-configs/{config_id}", response_model=VisionLLMConfigRead -) +@router.put("/vision-llm-configs/{config_id}", response_model=VisionLLMConfigRead) async def update_vision_llm_config( config_id: int, update_data: VisionLLMConfigUpdate, diff --git a/surfsense_backend/app/schemas/new_llm_config.py b/surfsense_backend/app/schemas/new_llm_config.py index a466f2c99..4fb3aa400 100644 --- a/surfsense_backend/app/schemas/new_llm_config.py +++ b/surfsense_backend/app/schemas/new_llm_config.py @@ -183,7 +183,8 @@ class LLMPreferencesRead(BaseModel): None, description="ID of the image generation config to use" ) vision_llm_config_id: int | None = Field( - None, description="ID of the vision LLM config to use for vision/screenshot analysis" + None, + description="ID of the vision LLM config to use for vision/screenshot analysis", ) agent_llm: dict[str, Any] | None = Field( None, description="Full config for agent LLM" @@ -214,5 +215,6 @@ class LLMPreferencesUpdate(BaseModel): None, description="ID of the image generation config to use" ) vision_llm_config_id: int | None = Field( - None, description="ID of the vision LLM config to use for vision/screenshot analysis" + None, + description="ID of the vision LLM config to use for vision/screenshot analysis", ) diff --git a/surfsense_backend/app/services/llm_service.py b/surfsense_backend/app/services/llm_service.py index e531aeabb..723b17607 100644 --- a/surfsense_backend/app/services/llm_service.py +++ b/surfsense_backend/app/services/llm_service.py @@ -434,9 +434,7 @@ async def get_vision_llm( config_id = search_space.vision_llm_config_id if config_id is None: - logger.error( - f"No vision LLM configured for search space {search_space_id}" - ) + logger.error(f"No vision LLM configured for search space {search_space_id}") return None if is_vision_auto_mode(config_id): diff --git a/surfsense_backend/app/services/vision_model_list_service.py b/surfsense_backend/app/services/vision_model_list_service.py index 09893dd06..fc459910b 100644 --- a/surfsense_backend/app/services/vision_model_list_service.py +++ b/surfsense_backend/app/services/vision_model_list_service.py @@ -15,7 +15,9 @@ import httpx logger = logging.getLogger(__name__) OPENROUTER_API_URL = "https://openrouter.ai/api/v1/models" -FALLBACK_FILE = Path(__file__).parent.parent / "config" / "vision_model_list_fallback.json" +FALLBACK_FILE = ( + Path(__file__).parent.parent / "config" / "vision_model_list_fallback.json" +) CACHE_TTL_SECONDS = 86400 # 24 hours _cache: list[dict] | None = None diff --git a/surfsense_backend/tests/unit/etl_pipeline/test_etl_pipeline_service.py b/surfsense_backend/tests/unit/etl_pipeline/test_etl_pipeline_service.py index 9add87879..9608b011d 100644 --- a/surfsense_backend/tests/unit/etl_pipeline/test_etl_pipeline_service.py +++ b/surfsense_backend/tests/unit/etl_pipeline/test_etl_pipeline_service.py @@ -319,7 +319,11 @@ async def test_llamacloud_with_azure_di_uses_azure_for_pdf(tmp_path, mocker): mocker.patch("app.config.config.ETL_SERVICE", "LLAMACLOUD") mocker.patch("app.config.config.LLAMA_CLOUD_API_KEY", "fake-key", create=True) - mocker.patch("app.config.config.AZURE_DI_ENDPOINT", "https://fake.cognitiveservices.azure.com/", create=True) + mocker.patch( + "app.config.config.AZURE_DI_ENDPOINT", + "https://fake.cognitiveservices.azure.com/", + create=True, + ) mocker.patch("app.config.config.AZURE_DI_KEY", "fake-key", create=True) fake_client = _mock_azure_di(mocker, "# Azure DI parsed") @@ -343,7 +347,11 @@ async def test_llamacloud_azure_di_fallback_on_failure(tmp_path, mocker): mocker.patch("app.config.config.ETL_SERVICE", "LLAMACLOUD") mocker.patch("app.config.config.LLAMA_CLOUD_API_KEY", "fake-key", create=True) - mocker.patch("app.config.config.AZURE_DI_ENDPOINT", "https://fake.cognitiveservices.azure.com/", create=True) + mocker.patch( + "app.config.config.AZURE_DI_ENDPOINT", + "https://fake.cognitiveservices.azure.com/", + create=True, + ) mocker.patch("app.config.config.AZURE_DI_KEY", "fake-key", create=True) mocker.patch( @@ -369,7 +377,11 @@ async def test_llamacloud_skips_azure_di_for_unsupported_ext(tmp_path, mocker): mocker.patch("app.config.config.ETL_SERVICE", "LLAMACLOUD") mocker.patch("app.config.config.LLAMA_CLOUD_API_KEY", "fake-key", create=True) - mocker.patch("app.config.config.AZURE_DI_ENDPOINT", "https://fake.cognitiveservices.azure.com/", create=True) + mocker.patch( + "app.config.config.AZURE_DI_ENDPOINT", + "https://fake.cognitiveservices.azure.com/", + create=True, + ) mocker.patch("app.config.config.AZURE_DI_KEY", "fake-key", create=True) fake_client = _mock_azure_di(mocker) @@ -424,7 +436,10 @@ async def test_llamacloud_heif_accepted_only_with_azure_di(tmp_path, mocker): EtlRequest(file_path=str(heif_file), filename="photo.heif") ) - mocker.patch("app.config.config.AZURE_DI_ENDPOINT", "https://fake.cognitiveservices.azure.com/") + mocker.patch( + "app.config.config.AZURE_DI_ENDPOINT", + "https://fake.cognitiveservices.azure.com/", + ) mocker.patch("app.config.config.AZURE_DI_KEY", "fake-key") fake_client = _mock_azure_di(mocker, "# HEIF from Azure DI") diff --git a/surfsense_backend/tests/unit/utils/test_file_extensions.py b/surfsense_backend/tests/unit/utils/test_file_extensions.py index c33b39f05..43dfef5f0 100644 --- a/surfsense_backend/tests/unit/utils/test_file_extensions.py +++ b/surfsense_backend/tests/unit/utils/test_file_extensions.py @@ -92,8 +92,9 @@ def test_non_document_extensions_are_not_supported(filename): # --------------------------------------------------------------------------- -def test_union_equals_all_three_sets(): +def test_union_includes_all_parser_extension_sets(): from app.utils.file_extensions import ( + AZURE_DI_DOCUMENT_EXTENSIONS, DOCLING_DOCUMENT_EXTENSIONS, DOCUMENT_EXTENSIONS, LLAMAPARSE_DOCUMENT_EXTENSIONS, @@ -104,6 +105,7 @@ def test_union_equals_all_three_sets(): DOCLING_DOCUMENT_EXTENSIONS | LLAMAPARSE_DOCUMENT_EXTENSIONS | UNSTRUCTURED_DOCUMENT_EXTENSIONS + | AZURE_DI_DOCUMENT_EXTENSIONS ) assert expected == DOCUMENT_EXTENSIONS diff --git a/surfsense_web/app/desktop/suggestion/suggestion.css b/surfsense_web/app/desktop/suggestion/suggestion.css index fd5ec5d3b..b27fe7874 100644 --- a/surfsense_web/app/desktop/suggestion/suggestion.css +++ b/surfsense_web/app/desktop/suggestion/suggestion.css @@ -169,7 +169,9 @@ body:has(.suggestion-body) { padding: 2px 4px; line-height: 1; border-radius: 4px; - transition: color 0.15s, background 0.15s; + transition: + color 0.15s, + background 0.15s; } .setup-dismiss:hover { diff --git a/surfsense_web/components/documents/DocumentNode.tsx b/surfsense_web/components/documents/DocumentNode.tsx index 898d0c8df..04cec5f89 100644 --- a/surfsense_web/components/documents/DocumentNode.tsx +++ b/surfsense_web/components/documents/DocumentNode.tsx @@ -191,9 +191,9 @@ export const DocumentNode = React.memo(function DocumentNode({ - - {doc.status?.reason || "Processing failed"} - + + {doc.status?.reason || "Processing failed"} + ); } @@ -222,79 +222,83 @@ export const DocumentNode = React.memo(function DocumentNode({ - - {getDocumentTypeIcon( - doc.document_type as DocumentTypeEnum, - "h-3.5 w-3.5 text-muted-foreground" - ) && ( - - {getDocumentTypeIcon( - doc.document_type as DocumentTypeEnum, - "h-3.5 w-3.5 text-muted-foreground" - )} - - )} - - - - + {getDocumentTypeIcon( + doc.document_type as DocumentTypeEnum, + "h-3.5 w-3.5 text-muted-foreground" + ) && ( + + {getDocumentTypeIcon( + doc.document_type as DocumentTypeEnum, + "h-3.5 w-3.5 text-muted-foreground" + )} + + )} + + + + e.stopPropagation()} + > + + + + e.stopPropagation()} > - - - - e.stopPropagation()}> - onPreview(doc)} disabled={isProcessing}> - - Open - - {isEditable && ( - onEdit(doc)}> - - Edit + onPreview(doc)} disabled={isProcessing}> + + Open - )} - onMove(doc)}> - - Move to... - - {onExport && ( - - - - Export - - - - - - )} - {onVersionHistory && isVersionableType(doc.document_type) && ( - onVersionHistory(doc)}> - - Versions + {isEditable && ( + onEdit(doc)}> + + Edit + + )} + onMove(doc)}> + + Move to... - )} - onDelete(doc)}> - - Delete - - - - + {onExport && ( + + + + Export + + + + + + )} + {onVersionHistory && isVersionableType(doc.document_type) && ( + onVersionHistory(doc)}> + + Versions + + )} + onDelete(doc)}> + + Delete + + + + diff --git a/surfsense_web/components/documents/FolderNode.tsx b/surfsense_web/components/documents/FolderNode.tsx index 01b01b8db..d9df69e9c 100644 --- a/surfsense_web/components/documents/FolderNode.tsx +++ b/surfsense_web/components/documents/FolderNode.tsx @@ -358,17 +358,17 @@ export const FolderNode = React.memo(function FolderNode({ - {isWatched && onRescan && ( - { - e.stopPropagation(); - handleRescan(); - }} - > - - Re-scan - - )} + {isWatched && onRescan && ( + { + e.stopPropagation(); + handleRescan(); + }} + > + + Re-scan + + )} {isWatched && onStopWatching && ( { @@ -407,15 +407,15 @@ export const FolderNode = React.memo(function FolderNode({ Move to... - { - e.stopPropagation(); - onDelete(folder); - }} - > - - Delete - + { + e.stopPropagation(); + onDelete(folder); + }} + > + + Delete + )} @@ -424,12 +424,12 @@ export const FolderNode = React.memo(function FolderNode({ {!isRenaming && contextMenuOpen && ( - {isWatched && onRescan && ( - handleRescan()}> - - Re-scan - - )} + {isWatched && onRescan && ( + handleRescan()}> + + Re-scan + + )} {isWatched && onStopWatching && ( onStopWatching(folder)}> @@ -448,10 +448,10 @@ export const FolderNode = React.memo(function FolderNode({ Move to... - onDelete(folder)}> - - Delete - + onDelete(folder)}> + + Delete + )} diff --git a/surfsense_web/components/layout/ui/sidebar/DocumentsSidebar.tsx b/surfsense_web/components/layout/ui/sidebar/DocumentsSidebar.tsx index a3dd2f649..c806c61d8 100644 --- a/surfsense_web/components/layout/ui/sidebar/DocumentsSidebar.tsx +++ b/surfsense_web/components/layout/ui/sidebar/DocumentsSidebar.tsx @@ -7,7 +7,6 @@ import { useParams } from "next/navigation"; import { useTranslations } from "next-intl"; import { useCallback, useEffect, useMemo, useState } from "react"; import { toast } from "sonner"; -import { DocumentsFilters } from "@/components/documents/DocumentsFilters"; import { sidebarSelectedDocumentsAtom } from "@/atoms/chat/mentioned-documents.atom"; import { connectorDialogOpenAtom } from "@/atoms/connector-dialog/connector-dialog.atoms"; import { connectorsAtom } from "@/atoms/connectors/connector-query.atoms"; @@ -18,12 +17,13 @@ import { openEditorPanelAtom } from "@/atoms/editor/editor-panel.atom"; import { rightPanelCollapsedAtom } from "@/atoms/layout/right-panel.atom"; import { CreateFolderDialog } from "@/components/documents/CreateFolderDialog"; import type { DocumentNodeDoc } from "@/components/documents/DocumentNode"; +import { DocumentsFilters } from "@/components/documents/DocumentsFilters"; import type { FolderDisplay } from "@/components/documents/FolderNode"; import { FolderPickerDialog } from "@/components/documents/FolderPickerDialog"; -import { FolderWatchDialog, type SelectedFolder } from "@/components/sources/FolderWatchDialog"; import { FolderTreeView } from "@/components/documents/FolderTreeView"; import { VersionHistoryDialog } from "@/components/documents/version-history"; import { EXPORT_FILE_EXTENSIONS } from "@/components/shared/ExportMenuItems"; +import { FolderWatchDialog, type SelectedFolder } from "@/components/sources/FolderWatchDialog"; import { AlertDialog, AlertDialogAction, @@ -109,8 +109,7 @@ export function DocumentsSidebar({ const folderPath = await api.selectFolder(); if (!folderPath) return; - const folderName = - folderPath.split("/").pop() || folderPath.split("\\").pop() || folderPath; + const folderName = folderPath.split("/").pop() || folderPath.split("\\").pop() || folderPath; setWatchInitialFolder({ path: folderPath, name: folderName }); setFolderWatchOpen(true); }, []); diff --git a/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx b/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx index 3dc6d2c01..371f3dc6d 100644 --- a/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx +++ b/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx @@ -21,9 +21,9 @@ import { import { useParams, useRouter } from "next/navigation"; import { useTranslations } from "next-intl"; import { useCallback, useDeferredValue, useEffect, useMemo, useRef, useState } from "react"; -import { getDocumentTypeLabel } from "@/components/documents/DocumentTypeIcon"; import { setTargetCommentIdAtom } from "@/atoms/chat/current-thread.atom"; import { convertRenderedToDisplay } from "@/components/chat-comments/comment-item/comment-item"; +import { getDocumentTypeLabel } from "@/components/documents/DocumentTypeIcon"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/animated-tabs"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; diff --git a/surfsense_web/components/shared/vision-config-dialog.tsx b/surfsense_web/components/shared/vision-config-dialog.tsx index 6a494e0a6..7332d3dcd 100644 --- a/surfsense_web/components/shared/vision-config-dialog.tsx +++ b/surfsense_web/components/shared/vision-config-dialog.tsx @@ -345,9 +345,7 @@ export function VisionConfigDialog({ - setFormData((p) => ({ ...p, model_name: val })) - } + onValueChange={(val) => setFormData((p) => ({ ...p, model_name: val }))} /> diff --git a/surfsense_web/components/sources/DocumentUploadTab.tsx b/surfsense_web/components/sources/DocumentUploadTab.tsx index a3a3297c1..663c0e7f9 100644 --- a/surfsense_web/components/sources/DocumentUploadTab.tsx +++ b/surfsense_web/components/sources/DocumentUploadTab.tsx @@ -26,12 +26,16 @@ import { Progress } from "@/components/ui/progress"; import { Spinner } from "@/components/ui/spinner"; import { Switch } from "@/components/ui/switch"; import { useElectronAPI } from "@/hooks/use-platform"; -import { getAcceptedFileTypes, getSupportedExtensions, getSupportedExtensionsSet } from "@/lib/supported-extensions"; import { trackDocumentUploadFailure, trackDocumentUploadStarted, trackDocumentUploadSuccess, } from "@/lib/posthog/events"; +import { + getAcceptedFileTypes, + getSupportedExtensions, + getSupportedExtensionsSet, +} from "@/lib/supported-extensions"; interface DocumentUploadTabProps { searchSpaceId: string; @@ -137,20 +141,24 @@ export function DocumentUploadTab({ if (!paths || paths.length === 0) return; const fileDataList = await electronAPI.readLocalFiles(paths); - const filtered = fileDataList.filter((fd: { name: string; data: ArrayBuffer; mimeType: string }) => { - const ext = fd.name.includes(".") ? `.${fd.name.split(".").pop()?.toLowerCase()}` : ""; - return ext !== "" && supportedExtensionsSet.has(ext); - }); + const filtered = fileDataList.filter( + (fd: { name: string; data: ArrayBuffer; mimeType: string }) => { + const ext = fd.name.includes(".") ? `.${fd.name.split(".").pop()?.toLowerCase()}` : ""; + return ext !== "" && supportedExtensionsSet.has(ext); + } + ); if (filtered.length === 0) { toast.error(t("no_supported_files_in_folder")); return; } - const newFiles: FileWithId[] = filtered.map((fd: { name: string; data: ArrayBuffer; mimeType: string }) => ({ - id: crypto.randomUUID?.() ?? `file-${Date.now()}-${Math.random().toString(36)}`, - file: new File([fd.data], fd.name, { type: fd.mimeType }), - })); + const newFiles: FileWithId[] = filtered.map( + (fd: { name: string; data: ArrayBuffer; mimeType: string }) => ({ + id: crypto.randomUUID?.() ?? `file-${Date.now()}-${Math.random().toString(36)}`, + file: new File([fd.data], fd.name, { type: fd.mimeType }), + }) + ); setFiles((prev) => [...prev, ...newFiles]); }, [electronAPI, supportedExtensionsSet, t]); @@ -255,14 +263,14 @@ export function DocumentUploadTab({ className="dark:bg-neutral-800" onClick={(e) => e.stopPropagation()} > - - - Files - - folderInputRef.current?.click()}> - - Folder - + + + Files + + folderInputRef.current?.click()}> + + Folder + ); @@ -320,8 +328,8 @@ export function DocumentUploadTab({ {/* MOBILE DROP ZONE */} - {hasContent ? ( - isElectron ? ( + {hasContent ? ( + isElectron ? ( {renderBrowseButton({ compact: true, fullWidth: true })} ) : ( Array.from(getSupportedExtensionsSet()), - [] - ); + const supportedExtensions = useMemo(() => Array.from(getSupportedExtensionsSet()), []); const handleSelectFolder = useCallback(async () => { const api = window.electronAPI; @@ -67,8 +64,7 @@ export function FolderWatchDialog({ const folderPath = await api.selectFolder(); if (!folderPath) return; - const folderName = - folderPath.split("/").pop() || folderPath.split("\\").pop() || folderPath; + const folderName = folderPath.split("/").pop() || folderPath.split("\\").pop() || folderPath; setSelectedFolder({ path: folderPath, name: folderName }); }, []); @@ -87,8 +83,7 @@ export function FolderWatchDialog({ file_extensions: supportedExtensions, }); - const rootFolderId = - (result as { root_folder_id?: number })?.root_folder_id ?? null; + const rootFolderId = (result as { root_folder_id?: number })?.root_folder_id ?? null; await api.addWatchedFolder({ path: selectedFolder.path, @@ -110,7 +105,14 @@ export function FolderWatchDialog({ } finally { setSubmitting(false); } - }, [selectedFolder, searchSpaceId, shouldSummarize, supportedExtensions, onOpenChange, onSuccess]); + }, [ + selectedFolder, + searchSpaceId, + shouldSummarize, + supportedExtensions, + onOpenChange, + onSuccess, + ]); const handleOpenChange = useCallback( (nextOpen: boolean) => { @@ -128,18 +130,14 @@ export function FolderWatchDialog({ Watch Local Folder - - Select a folder to sync and watch for changes. - + Select a folder to sync and watch for changes. {selectedFolder ? ( - - {selectedFolder.name} - + {selectedFolder.name} {selectedFolder.path} @@ -173,20 +171,11 @@ export function FolderWatchDialog({ Improves search quality but adds latency - + - - - Start Folder Sync - + + Start Folder Sync {submitting && ( diff --git a/surfsense_web/contracts/enums/vision-providers.ts b/surfsense_web/contracts/enums/vision-providers.ts index 08be93b74..477fd5c53 100644 --- a/surfsense_web/contracts/enums/vision-providers.ts +++ b/surfsense_web/contracts/enums/vision-providers.ts @@ -107,22 +107,62 @@ export const VISION_MODELS: LLMModel[] = [ { value: "gpt-4o", label: "GPT-4o", provider: "OPENAI", contextWindow: "128K" }, { value: "gpt-4o-mini", label: "GPT-4o Mini", provider: "OPENAI", contextWindow: "128K" }, { value: "gpt-4-turbo", label: "GPT-4 Turbo", provider: "OPENAI", contextWindow: "128K" }, - { value: "claude-sonnet-4-20250514", label: "Claude Sonnet 4", provider: "ANTHROPIC", contextWindow: "200K" }, - { value: "claude-3-7-sonnet-20250219", label: "Claude 3.7 Sonnet", provider: "ANTHROPIC", contextWindow: "200K" }, - { value: "claude-3-5-sonnet-20241022", label: "Claude 3.5 Sonnet", provider: "ANTHROPIC", contextWindow: "200K" }, - { value: "claude-3-opus-20240229", label: "Claude 3 Opus", provider: "ANTHROPIC", contextWindow: "200K" }, - { value: "claude-3-haiku-20240307", label: "Claude 3 Haiku", provider: "ANTHROPIC", contextWindow: "200K" }, + { + value: "claude-sonnet-4-20250514", + label: "Claude Sonnet 4", + provider: "ANTHROPIC", + contextWindow: "200K", + }, + { + value: "claude-3-7-sonnet-20250219", + label: "Claude 3.7 Sonnet", + provider: "ANTHROPIC", + contextWindow: "200K", + }, + { + value: "claude-3-5-sonnet-20241022", + label: "Claude 3.5 Sonnet", + provider: "ANTHROPIC", + contextWindow: "200K", + }, + { + value: "claude-3-opus-20240229", + label: "Claude 3 Opus", + provider: "ANTHROPIC", + contextWindow: "200K", + }, + { + value: "claude-3-haiku-20240307", + label: "Claude 3 Haiku", + provider: "ANTHROPIC", + contextWindow: "200K", + }, { value: "gemini-2.5-flash", label: "Gemini 2.5 Flash", provider: "GOOGLE", contextWindow: "1M" }, { value: "gemini-2.5-pro", label: "Gemini 2.5 Pro", provider: "GOOGLE", contextWindow: "1M" }, { value: "gemini-2.0-flash", label: "Gemini 2.0 Flash", provider: "GOOGLE", contextWindow: "1M" }, { value: "gemini-1.5-pro", label: "Gemini 1.5 Pro", provider: "GOOGLE", contextWindow: "1M" }, { value: "gemini-1.5-flash", label: "Gemini 1.5 Flash", provider: "GOOGLE", contextWindow: "1M" }, - { value: "pixtral-large-latest", label: "Pixtral Large", provider: "MISTRAL", contextWindow: "128K" }, + { + value: "pixtral-large-latest", + label: "Pixtral Large", + provider: "MISTRAL", + contextWindow: "128K", + }, { value: "pixtral-12b-2409", label: "Pixtral 12B", provider: "MISTRAL", contextWindow: "128K" }, { value: "grok-2-vision-1212", label: "Grok 2 Vision", provider: "XAI", contextWindow: "32K" }, { value: "llava", label: "LLaVA", provider: "OLLAMA" }, { value: "bakllava", label: "BakLLaVA", provider: "OLLAMA" }, { value: "llava-llama3", label: "LLaVA Llama 3", provider: "OLLAMA" }, - { value: "llama-4-scout-17b-16e-instruct", label: "Llama 4 Scout 17B", provider: "GROQ", contextWindow: "128K" }, - { value: "meta-llama/Llama-4-Scout-17B-16E-Instruct", label: "Llama 4 Scout 17B", provider: "TOGETHER_AI", contextWindow: "128K" }, + { + value: "llama-4-scout-17b-16e-instruct", + label: "Llama 4 Scout 17B", + provider: "GROQ", + contextWindow: "128K", + }, + { + value: "meta-llama/Llama-4-Scout-17B-16E-Instruct", + label: "Llama 4 Scout 17B", + provider: "TOGETHER_AI", + contextWindow: "128K", + }, ]; diff --git a/surfsense_web/lib/supported-extensions.ts b/surfsense_web/lib/supported-extensions.ts index a50a28cc6..f615b3d46 100644 --- a/surfsense_web/lib/supported-extensions.ts +++ b/surfsense_web/lib/supported-extensions.ts @@ -80,9 +80,7 @@ export function getAcceptedFileTypes(): Record { return FILE_TYPE_CONFIG[etlService || "default"] || FILE_TYPE_CONFIG.default; } -export function getSupportedExtensions( - acceptedFileTypes?: Record -): string[] { +export function getSupportedExtensions(acceptedFileTypes?: Record): string[] { const types = acceptedFileTypes ?? getAcceptedFileTypes(); return Array.from(new Set(Object.values(types).flat())).sort(); }
- {selectedFolder.name} -
{selectedFolder.name}
{selectedFolder.path}