From 81a79075df793495eebe699c1f357e9ee16f9092 Mon Sep 17 00:00:00 2001 From: chengke <404835780@qq.com> Date: Tue, 2 Jun 2026 15:43:00 +0800 Subject: [PATCH] fix(sources): raise Notebook upload limit to 300MB Align client validation, Blob staging, and upload UI copy with the 300MB backend file-size limit while surfacing support contact details for oversized files. Refs Ontos-AI/knowhere#121 Co-authored-by: Cursor --- src/app/api/source-uploads/blob/route.test.ts | 2 +- src/components/chunks-panel.tsx | 4 +++- src/components/source-upload-dialog.tsx | 6 ++++-- src/components/sources-panel.test.ts | 15 +++++++-------- src/domains/sources/blob-upload.test.ts | 16 ++++++++++++++++ src/domains/sources/blob-upload.ts | 3 ++- src/domains/sources/validation.test.ts | 9 ++++++--- src/domains/sources/validation.ts | 10 ++++++++-- 8 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/app/api/source-uploads/blob/route.test.ts b/src/app/api/source-uploads/blob/route.test.ts index ae20700..509a803 100644 --- a/src/app/api/source-uploads/blob/route.test.ts +++ b/src/app/api/source-uploads/blob/route.test.ts @@ -29,7 +29,7 @@ describe("POST /api/source-uploads/blob", () => { mocks.getCurrentUser.mockResolvedValue({ id: "user_1" }); }); - it("generates a public client-upload token capped at the 100 MB document limit", async () => { + it("generates a public client-upload token capped at the document upload limit", async () => { mocks.handleUpload.mockImplementation(async (options) => { const tokenOptions = await options.onBeforeGenerateToken( "source-uploads/upload_1/document.pdf", diff --git a/src/components/chunks-panel.tsx b/src/components/chunks-panel.tsx index b27e341..5be4120 100644 --- a/src/components/chunks-panel.tsx +++ b/src/components/chunks-panel.tsx @@ -38,6 +38,7 @@ import { import { useChunksPanelWorkflow } from "@/components/chunks-panel-workflow"; import { ParsedChunkCard } from "@/components/parsed-chunk-card"; import { chunksPanelState } from "@/components/chunks-panel-state"; +import { MAX_UPLOAD_MB } from "@/domains/sources/validation"; import { useSourceOriginalPreviewWarmup } from "@/components/source-original-preview-warmup"; import { sourceOriginalPreviewModel } from "@/components/source-original-preview-model"; import type { ParsedChunkView } from "@/domains/chunks/types"; @@ -1006,7 +1007,8 @@ function EmptySourceUploadState({ parsed chunks and chat. - PDF, DOCX, TXT, MD, spreadsheets, slides, and images up to 100 MB + PDF, DOCX, TXT, MD, spreadsheets, slides, and images up to{" "} + {MAX_UPLOAD_MB} MB )} diff --git a/src/components/source-upload-dialog.tsx b/src/components/source-upload-dialog.tsx index 55bf8cd..8c3165b 100644 --- a/src/components/source-upload-dialog.tsx +++ b/src/components/source-upload-dialog.tsx @@ -18,6 +18,7 @@ import { } from "@/components/ui/dialog"; import { useSourceUploadDialogWorkflow } from "@/components/source-upload-dialog-workflow"; import type { SourceView } from "@/domains/sources/types"; +import { MAX_UPLOAD_MB } from "@/domains/sources/validation"; export type SourceUploadDialogProps = { readonly onSourceUploaded?: (source: SourceView) => void; @@ -84,7 +85,8 @@ export function SourceUploadDialog({ Add source Add a document to your notebook. Notebook accepts PDF, DOC, DOCX, - TXT, MD, XLS, XLSX, PPTX, images, and more files up to 100 MB. + TXT, MD, XLS, XLSX, PPTX, images, and more files up to{" "} + {MAX_UPLOAD_MB} MB.

- Max size: 100 MB + Max size: {MAX_UPLOAD_MB} MB

{selectedFileName && (

diff --git a/src/components/sources-panel.test.ts b/src/components/sources-panel.test.ts index bf6daae..068d509 100644 --- a/src/components/sources-panel.test.ts +++ b/src/components/sources-panel.test.ts @@ -24,6 +24,9 @@ import { SourcesPanel } from "./sources-panel"; const C = SourcesPanel as React.FC>; const originalResizeObserver = globalThis.ResizeObserver; +const fileTooLargeMessage = + "File is too large. Upload a document up to 300 MB. " + + "For larger files, contact team@knowhereto.ai or open an issue at https://github.com/Ontos-AI/knowhere/issues."; describe("SourcesPanel", () => { beforeEach(() => { @@ -131,10 +134,10 @@ describe("SourcesPanel", () => { expect( screen.getByText( - /Notebook accepts PDF, DOC, DOCX, TXT, MD, XLS, XLSX, PPTX, images, and more files up to 100 MB/, + /Notebook accepts PDF, DOC, DOCX, TXT, MD, XLS, XLSX, PPTX, images, and more files up to 300 MB/, ), ).toBeTruthy(); - expect(screen.getByText("Max size: 100 MB")).toBeTruthy(); + expect(screen.getByText("Max size: 300 MB")).toBeTruthy(); expect(opened.container.textContent).not.toMatch(/Knowhere|parsing|indexing/i); }); @@ -383,7 +386,7 @@ describe("SourcesPanel", () => { } return Response.json( - { message: "File is too large. Upload a document up to 100 MB." }, + { message: fileTooLargeMessage }, { status: 400 }, ); }); @@ -407,11 +410,7 @@ describe("SourcesPanel", () => { } fireEvent.submit(form); - expect( - await screen.findByText( - "File is too large. Upload a document up to 100 MB.", - ), - ).toBeTruthy(); + expect(await screen.findByText(fileTooLargeMessage)).toBeTruthy(); expect(screen.getByRole("button", { name: "Confirm" })).toBeTruthy(); }); }); diff --git a/src/domains/sources/blob-upload.test.ts b/src/domains/sources/blob-upload.test.ts index e5addf4..b80374e 100644 --- a/src/domains/sources/blob-upload.test.ts +++ b/src/domains/sources/blob-upload.test.ts @@ -4,6 +4,7 @@ import { getSourceUploadBlobPathname, validateSourceBlobUploadInput, } from "./blob-upload"; +import { FILE_TOO_LARGE_MESSAGE, MAX_UPLOAD_BYTES } from "./validation"; describe("source Blob upload metadata", () => { afterEach(() => { @@ -62,4 +63,19 @@ describe("source Blob upload metadata", () => { message: "Invalid upload URL. Choose the document again.", }); }); + + it("rejects staged upload metadata larger than the upload limit", () => { + const result = validateSourceBlobUploadInput({ + pathname: "source-uploads/upload_1/document.pdf", + url: "https://store.public.blob.vercel-storage.com/source-uploads/upload_1/document.pdf", + fileName: "oversized.pdf", + mimeType: "application/pdf", + sizeBytes: MAX_UPLOAD_BYTES + 1, + }); + + expect(result).toEqual({ + ok: false, + message: FILE_TOO_LARGE_MESSAGE, + }); + }); }); diff --git a/src/domains/sources/blob-upload.ts b/src/domains/sources/blob-upload.ts index 309bd7c..4390c19 100644 --- a/src/domains/sources/blob-upload.ts +++ b/src/domains/sources/blob-upload.ts @@ -1,4 +1,5 @@ import { + FILE_TOO_LARGE_MESSAGE, MAX_UPLOAD_BYTES, validateUploadFile, type UploadValidationResult, @@ -123,7 +124,7 @@ export function validateSourceBlobUploadMetadata( if (input.sizeBytes > MAX_UPLOAD_BYTES) { return { ok: false, - message: "File is too large. Upload a document up to 100 MB.", + message: FILE_TOO_LARGE_MESSAGE, }; } diff --git a/src/domains/sources/validation.test.ts b/src/domains/sources/validation.test.ts index 8b82eb2..bbe3c37 100644 --- a/src/domains/sources/validation.test.ts +++ b/src/domains/sources/validation.test.ts @@ -1,12 +1,14 @@ import { describe, expect, it } from "vitest"; import { + FILE_TOO_LARGE_MESSAGE, MAX_UPLOAD_BYTES, + MAX_UPLOAD_MB, validateUploadFile, } from "./validation"; describe("validateUploadFile", () => { - it("accepts supported document extensions within 100 MB", () => { + it("accepts supported document extensions within the upload limit", () => { const result = validateUploadFile({ name: "lecture-notes.PDF", type: "application/pdf", @@ -70,7 +72,7 @@ describe("validateUploadFile", () => { }); }); - it("rejects files larger than the 100 MB limit", () => { + it("rejects files larger than the upload limit", () => { const result = validateUploadFile({ name: "large.pdf", type: "application/pdf", @@ -79,7 +81,8 @@ describe("validateUploadFile", () => { expect(result).toEqual({ ok: false, - message: "File is too large. Upload a document up to 100 MB.", + message: FILE_TOO_LARGE_MESSAGE, }); + expect(MAX_UPLOAD_MB).toBe(300); }); }); diff --git a/src/domains/sources/validation.ts b/src/domains/sources/validation.ts index 8fa0b92..bdeda20 100644 --- a/src/domains/sources/validation.ts +++ b/src/domains/sources/validation.ts @@ -1,4 +1,10 @@ -export const MAX_UPLOAD_BYTES = 100 * 1024 * 1024; +export const MAX_UPLOAD_MB = 300; +export const MAX_UPLOAD_BYTES = MAX_UPLOAD_MB * 1024 * 1024; +export const SUPPORT_EMAIL = "team@knowhereto.ai"; +export const KNOWHERE_ISSUES_URL = "https://github.com/Ontos-AI/knowhere/issues"; +export const FILE_TOO_LARGE_MESSAGE = + `File is too large. Upload a document up to ${MAX_UPLOAD_MB} MB. ` + + `For larger files, contact ${SUPPORT_EMAIL} or open an issue at ${KNOWHERE_ISSUES_URL}.`; const SUPPORTED_EXTENSIONS = new Set([ "doc", @@ -36,7 +42,7 @@ export function validateUploadFile(file: UploadFileInfo): UploadValidationResult if (file.size > MAX_UPLOAD_BYTES) { return { ok: false, - message: "File is too large. Upload a document up to 100 MB.", + message: FILE_TOO_LARGE_MESSAGE, }; }