Skip to content

📄 feat: Add Local AnyDoc and pdf-inspector Document Extraction - #14701

Draft
berry-13 wants to merge 21 commits into
devfrom
ocr/pdf-inspector-evaluation
Draft

berry-13 wants to merge 21 commits into
devfrom
ocr/pdf-inspector-evaluation

Conversation

@berry-13

@berry-13 berry-13 commented Aug 8, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

This PR adds server-side local document extraction with AnyDoc and pdf-inspector while keeping configured OCR services as a separate fallback layer.

AnyDoc and pdf-inspector are internal engines behind LibreChat's existing document_parser strategy. They are not independently selectable OCR providers and they do not run in the browser. AnyDoc uses Rust-backed N-API code for its supported formats, and pdf-inspector is also Rust-backed. LibreChat runs both native bindings in operating-system child processes behind one shared concurrency limit.

PDFs are routed directly to LibreChat's pdf-inspector module even though AnyDoc can parse PDFs upstream. That keeps page accounting, image-only page handling, native failure recovery, and the PDF.js work limit inside one provider-specific boundary.

Architecture overview

flowchart TD
  subgraph Browser["Browser"]
    Pick["Select file"]
    Infer["Canonicalize generic MIME from filename"]
    ClientGate{"Known document allowed by local parser<br/>or configured OCR route?"}
    Preview["Display server-extracted text"]
  end

  subgraph Admission["Server admission"]
    Multer["Multer uses the same MIME inference"]
    ServerGate{"Allowed by endpoint configuration?"}
  end

  subgraph Parser["Local document_parser strategy"]
    Dispatch{"Effective MIME is PDF?"}
    PDF["pdf-inspector adapter"]
    AnyDoc["AnyDoc adapter"]
    PDFJS["Bounded PDF.js recovery"]
    Result{"Complete local text?"}
  end

  subgraph OCRBoundary["Configured OCR boundary"]
    OCREligible{"OCR strategy configured,<br/>MIME allowed, capability granted?"}
    OCR["Mistral, Azure, Vertex,<br/>or custom OCR"]
  end

  Store["Persist text, original MIME,<br/>and parser provenance"]
  Partial["Persist partial text with<br/>bounded omission notice"]
  Refuse["Hard refusal or extraction error"]

  Pick --> Infer --> ClientGate
  ClientGate -->|Yes| Multer --> ServerGate
  ClientGate -->|No| Refuse
  ServerGate -->|Yes| Dispatch
  ServerGate -->|No| Refuse
  Dispatch -->|PDF| PDF
  Dispatch -->|Other document| AnyDoc
  PDF -->|Native parser failure| PDFJS
  PDF --> Result
  PDFJS --> Result
  AnyDoc --> Result
  Result -->|Complete| Store --> Preview
  Result -->|Missing text or pages| OCREligible
  OCREligible -->|Yes| OCR --> Store
  OCREligible -->|No, partial text exists| Partial --> Preview
  OCREligible -->|No local text| Refuse
Loading

Native parser process boundary

Both native engines use the same process-wide limiter. At most two parser children run per API process, so AnyDoc and PDF bursts cannot multiply independently.

flowchart LR
  UploadA["AnyDoc request"] --> Queue["Shared FIFO limiter<br/>2 active slots"]
  UploadP["PDF request"] --> Queue
  Burst["Additional concurrent uploads"] --> Queue

  Queue --> Child1["Node child process 1"]
  Queue --> Child2["Node child process 2"]

  Child1 --> Native1["Load native binding<br/>read and parse file"]
  Child2 --> Native2["Load native binding<br/>read and parse file"]

  Native1 -->|IPC result| API["API process"]
  Native2 -->|IPC result| API
  Native1 -->|Abort or segfault| Exit1["Only child exits"]
  Native2 -->|30 second timeout| Kill2["SIGKILL child"]

  Exit1 --> API
  Kill2 --> API
  API --> Fallback["Provider-specific fallback or error"]
Loading

This boundary protects the API process from native aborts and segmentation faults. It also makes the 30 second timeout capable of terminating a synchronous native call. AnyDoc's binding is resolved and loaded only inside the child.

Configuration precedence and OCR routing

fileConfig.documentParser.supportedMimeTypes remains authoritative for local parsing. Excluding a known document from that list does not block a deliberately configured OCR route for the same MIME type.

flowchart TD
  Type["Known document MIME"] --> Local{"Allowed by<br/>documentParser allowlist?"}
  Local -->|Yes| Context["Offer context upload"]
  Local -->|No| OCRConfigured{"Server reports an OCR<br/>strategy is configured?"}
  OCRConfigured -->|No| Reject["Do not offer context upload"]
  OCRConfigured -->|Yes| OCRType{"MIME allowed by<br/>ocr.supportedMimeTypes?"}
  OCRType -->|No| Reject
  OCRType -->|Yes| Capability{"Agent has OCR capability?"}
  Capability -->|No| Reject
  Capability -->|Yes| Context
Loading

The server adds a derived ocr.enabled signal to the file configuration response. The client cannot infer availability from the default OCR MIME catalog, because a catalog alone does not mean that an OCR strategy exists. Upload validation and viable destination routing use this signal together with the agent capability.

Shared parsed-text previews

Locally parsed documents have durable extracted text but no durable source binary at their parser marker path. Shared links therefore snapshot a preview-only marker rather than a temp path or the extracted text itself.

sequenceDiagram
  participant Owner as Share creator
  participant Share as SharedLink snapshot
  participant Viewer as Shared viewer
  participant Route as Share file route
  participant File as Live File record

  Owner->>Share: Snapshot metadata and hasTextPreview=true
  Note over Share: No temp filepath and no extracted text copy
  Viewer->>Route: GET shared preview
  Route->>Share: Authorize file_id against snapshot
  Share-->>Route: Preview-only marker
  Route->>File: Read durable extracted text
  File-->>Viewer: Text preview, no-store
  Viewer->>Route: Request binary download
  Route-->>Viewer: 404, no shared binary
Loading

Shared-message sanitization exposes only hasTextPreview, not source, storage paths, or text. The preview route still requires the file to be present in the shared-link snapshot, checks the live record against the snapshotted revision, and refuses binary streaming for preview-only records.

Provider boundaries

  • Keeps document_parser as the only public upload strategy for local document extraction.
  • Restricts OCRStrategy to actual OCR services: Mistral OCR, custom OCR, Azure Mistral OCR, and Vertex AI Mistral OCR.
  • Removes AnyDoc and pdf-inspector from the public OCR registry and top-level API exports.
  • Retains anydoc and pdf_inspector only as persistence provenance values.
  • Imports both engines from the document parser module, preventing other upload paths from selecting them as standalone providers.
  • Leaves the browser responsible for upload eligibility and preview rendering only.

File-type ownership and admission

  • Makes fileConfig.documentParser.supportedMimeTypes authoritative for local parsing of known document types.
  • Uses the built-in parser list only when administrator configuration does not replace it.
  • Allows a locally excluded document through context admission only when server-derived OCR availability, the configured OCR MIME list, and the agent OCR capability all agree.
  • Adds document parser types to client context eligibility without letting permissive text or default OCR matchers override the parser allowlist.
  • Moves generic MIME canonicalization into shared inference used by both the browser and Multer.
  • Resolves application/octet-stream and binary/octet-stream from the filename before client or server admission.
  • Canonicalizes generic PDF uploads before dispatch so they always reach pdf-inspector.
  • Aligns AnyDoc's Excel MIME aliases with the shared legacy and extensionless Excel catalog.
  • Supports macro-enabled Office, legacy slideshow, OpenDocument, RTF, EPUB, CSV, and PDF filename inference.

PDF pipeline

  • Routes PDFs directly to the pdf-inspector adapter.
  • Runs the Rust-backed binding in a killable child process behind the shared native parser limit.
  • Keeps per-page extraction, incomplete embedded OCR recovery, image-only page accounting, and fallback policy inside the PDF module.
  • Uses bounded PDF.js recovery when native parsing fails.
  • Enforces the 250-page limit before fallback traversal with a typed PdfPageLimitError.
  • Treats that page limit as a hard refusal, so page-flooded PDFs cannot fall through to paid or self-hosted OCR.
  • Preserves pagesNeedingOcr in the PDF.js fallback so mixed text and image PDFs do not silently lose pages.
  • Summarizes large missing-page lists in logs and persisted notices.

AnyDoc pipeline and archive safety

  • Uses @firecrawl/anydoc for supported non-PDF Office, OpenDocument, ebook, structured text, and markup formats.
  • Loads and invokes the AnyDoc native binding only inside a child process.
  • Keeps provider-specific type validation and content-detection warnings in the AnyDoc module.
  • Guards ZIP-based formats before native parsing by limiting declared entries, total uncompressed size, and compression-ratio abuse.
  • Propagates ZIP-bomb refusals without forwarding rejected archives to another parser or configured OCR service.
  • Keeps PDF excluded from AnyDoc's LibreChat entry point even though upstream AnyDoc accepts it.
  • Keeps parser fixtures out of the published API package.

OCR fallback and partial text

  • Invokes configured OCR only after local parsing fails or reports missing PDF pages.
  • Requires the effective MIME type to be enabled by OCR configuration.
  • Does not require OCR capability for complete local extraction.
  • Preserves useful partial local text when OCR exists but the current agent lacks OCR capability.
  • Raises the capability error when no local text exists.
  • Avoids paid OCR when local extraction completed successfully.
  • Propagates ZIP-bomb and PDF page-limit refusals before the fallback boundary.

Persistence and preview behavior

  • Preserves the original document MIME type for locally extracted text.
  • Stores engine provenance without exposing the engine as a public strategy.
  • Prevents binary Office data from falling through to native plain-text handling.
  • Shows extracted text directly for parsed PDFs and Office documents without probing a nonexistent binary path.
  • Makes that text preview available to authorized shared-link viewers through a share-safe marker.
  • Adds accessible focus styling and alert semantics for extraction errors.

Change Type

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)

Testing

  • Focused parser, upload admission, OCR routing, shared-link, and preview regression suites passed.
  • API server aggregate run passed 178 suites and 3,479 tests before two Jest workers exhausted their cumulative 4 GB heaps. The two suites that could not start then passed independently: 94 of 94 agent-client tests and 20 of 20 people-picker tests. This executes all 180 non-skipped suites and 3,593 tests locally; 2 suites and 19 tests remain intentionally skipped by repository configuration.
  • Full client execution passed 315 suites and 3,998 tests. The generated JUnit report records zero failures and zero errors. The local npm wrapper returned 1 after the green Jest summary in both coverage and non-coverage modes, with no configured coverage threshold.
  • The full @librechat/api coverage run executed all suites. One unrelated load-sensitive YouTube safety assertion took 1,069 ms against a 1,000 ms limit under the full serial load. Its exact 61-test suite reran independently with all tests passing and the assertion completing in 29 ms.
  • Full data-provider suite passed: 28 suites, 1,516 tests passed, 1 skipped.
  • Full data-schemas suite passed: 58 suites and 1,966 tests.
  • Exact-file ESLint passed for all 30 changed JavaScript and TypeScript files, including the commit hook's final ESLint pass.
  • Prettier and import ordering passed for all changed files.
  • The repository-wide lint command is blocked before file discovery by the local installed minimatch tree: TypeError: expand is not a function. No source lint finding is produced by that command.
  • TypeScript checks passed for API, client, data-provider, and data-schemas.
  • API, data-provider, data-schemas, and frontend CI builds passed.
  • Circular dependency checks passed for @librechat/api, data-provider, data-schemas, the client package, and the API server.
  • git diff --check, the added-line punctuation audit, and the UI accessibility static scan passed.

Test Configuration:

  • Linux worktree with repository lockfile dependencies and Node.js 24.16.0.
  • Native pdf-inspector and AnyDoc Node.js bindings loaded through @librechat/api tests, including real child-process execution.
  • PDF fixtures cover text PDFs, malformed cross-reference tables, scanned pages, mixed text and image pages, native failure, concurrency limiting, page limits, and page-level recovery.
  • Office fixtures cover DOCX, PPTX, legacy Excel aliases, archive-limit rejection, generic MIME types, and the supported MIME table.
  • Provider-backed Anthropic integration tests were run with the local key cleared, matching CI and avoiding an unintended paid external call.
  • Browser E2E was not run because this patch changes preview selection and authorization, not browser-side parsing. Component, route, share snapshot, and full client tests cover the changed user-visible flow.

Checklist

  • My code adheres to this project's style guidelines
  • I have performed a self-review of my own code
  • I have commented in any complex areas of my code
  • My changes do not introduce new source warnings
  • I have written tests demonstrating that my changes are effective or that my feature works
  • Local unit tests covering every changed path pass with my changes

Copilot AI lite review requested due to automatic review settings August 8, 2026 01:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@berry-13 berry-13 changed the title feat: add local AnyDoc and pdf-inspector document extraction 📄 feat: Add Local AnyDoc and pdf-inspector Document Extraction Aug 8, 2026
@berry-13

berry-13 commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4dd57cd08e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/api/src/files/pdfInspector/native.ts Outdated
Comment thread packages/api/src/files/pdfInspector/crud.ts Outdated
Comment thread api/server/services/Files/process.js Outdated
Comment thread api/server/services/Files/process.js Outdated
Comment thread api/server/services/Files/process.js
Comment thread client/src/components/Chat/Messages/Content/FilePreviewDialog.tsx Outdated
Comment thread api/server/services/Files/process.js
@danny-avila

Copy link
Copy Markdown
Collaborator

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5cddc4e3fe

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/api/src/files/documents/crud.ts Outdated
Comment thread packages/data-provider/src/file-config.ts
@berry-13

berry-13 commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d1aa3520ff

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread api/server/services/Files/process.js Outdated
Comment thread packages/api/src/files/pdfInspector/crud.ts Outdated
Comment thread packages/api/src/files/anydoc/crud.ts Outdated
Comment thread client/src/utils/files.ts Outdated
@berry-13

berry-13 commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dccc4b2c49

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/api/src/files/anydoc/crud.ts Outdated
Comment thread packages/api/src/files/pdfInspector/native.ts Outdated
Comment thread api/server/services/Files/process.js
Comment thread client/src/utils/files.ts Outdated
Comment thread client/src/components/Chat/Messages/Content/FilePreviewDialog.tsx Outdated
@berry-13

berry-13 commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 95d4a599b6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/data-schemas/src/methods/share.ts Outdated
Comment thread packages/data-provider/src/config.ts
@berry-13

berry-13 commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4c1ec96f32

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread api/server/services/Files/process.js Outdated
Comment thread api/server/services/Files/process.js Outdated
Comment thread packages/data-schemas/src/methods/share.ts Outdated
@berry-13

berry-13 commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 185e2b657a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/api/src/files/documents/nativeProcess.ts Outdated
Comment thread packages/api/src/files/documents/media.ts Outdated
Comment thread client/src/utils/files.ts Outdated
Comment thread api/server/services/Files/process.js Outdated
Comment thread packages/api/src/files/pdfInspector/crud.ts Outdated
@berry-13

berry-13 commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2b17419725

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/data-schemas/src/methods/share.ts Outdated
Comment thread packages/api/src/files/documents/zipSafety.ts Outdated
Comment thread packages/api/src/files/anydoc/native.ts
Comment thread client/src/components/Chat/Messages/Content/FilePreviewDialog.tsx Outdated
@berry-13

berry-13 commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7df75cc142

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/api/src/files/documents/media.ts Outdated
Comment thread packages/api/src/files/pdfInspector/native.ts
@berry-13

berry-13 commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 11562c7692

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread api/server/services/Files/process.js Outdated
Comment thread packages/data-provider/src/file-config.ts
Comment thread packages/data-provider/src/file-config.ts Outdated
@berry-13

berry-13 commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 87cc745ea1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/api/src/files/pdfInspector/crud.ts Outdated
Comment thread packages/data-schemas/src/methods/share.ts Outdated
@berry-13

berry-13 commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4ec7543db0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/api/src/files/documents/pdfjs.ts Outdated
Comment thread client/src/utils/files.ts Outdated
@berry-13

berry-13 commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8682c4e904

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/api/src/files/documents/nativeProcess.ts Outdated
Comment thread packages/api/src/files/pdfInspector/crud.ts Outdated
Comment thread packages/api/src/utils/promise.ts
Comment thread packages/api/src/files/documents/zipSafety.ts Outdated
@berry-13

berry-13 commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 18185de7e8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

snapshotFiles: { $ne: false },
$or: [
{ fileSnapshots: { $exists: false } },
{ snapshotVersion: { $exists: false } },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Guard backfills against legacy-replica republishes

At reviewed head 18185de7e8ba37e1a40af53e1f6145b91bed4464, during a rolling mixed-version deployment, an older replica can republish this share after the viewer read it, replacing fileSnapshots while leaving snapshotVersion absent; this predicate still matches, so the viewer's stale backfill overwrites the republished snapshot and can re-authorize files the owner just removed. Include the originally read snapshot or publication revision in the compare-and-set rather than treating every absent version as unchanged.

AGENTS.md reference: AGENTS.md:L45-L47

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b34a507: persistBackfilledSnapshots now compares the stored fileSnapshots against the array the viewer read (or requires it absent), so a republish by an older replica that leaves snapshotVersion unset no longer matches. share.test.ts 'keeps a legacy replica republish that lands during a version upgrade' fails without the fix and passes with it.

@berry-13

berry-13 commented Oct 3, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

Please review the current PR head b34a507. State the exact reviewed commit and ignore findings that apply only to earlier heads.

Purpose supplied by the requester: 4 fixes since reviewed head 18185de: upload fallback parser bounds, fallback partial-text policy, streamed OpenAI upload, share backfill CAS on the read snapshot

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b34a50798d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +142 to +144
? extractors.parseDocument({
file,
maxFileSize: documentParser?.fileSizeLimit,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Cancel fallback parsing when the upload disconnects

At reviewed commit b34a50798d08307122df7a2fadb41d216fdf68ef, when textFallbackWithoutTools parses a none-routed document and the client disconnects, this separate parser invocation still receives no request-owned AbortSignal; unlike the primary Context parser, it can retain an admission slot until its deadline and then continue into storage after the upload was cancelled. Fresh evidence beyond the resolved primary-parser thread is this fallback path at process.js:1307-1316; thread the response-close signal through here and rethrow cancellation instead of converting it to a best-effort missing fallback.

AGENTS.md reference: AGENTS.md:L45-L47

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c55cdda: resolveUploadFallbackText takes the upload response as its connection, aborts the parse on close (freeing the admission slot) and rethrows cancellation instead of returning a missing fallback; the listener is removed in finally. Covered by fallback.spec.ts 'cancels the parse when the upload closes'.

Comment on lines +453 to +456
fileSnapshots: readSnapshots === undefined ? { $exists: false } : readSnapshots,
$or: [
{ snapshotVersion: { $exists: false } },
{ snapshotVersion: { $lt: FILE_SNAPSHOT_VERSION } },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Compare the publication revision during snapshot backfill

When a version-1 share contains snapshot [A] but its old snapshot omitted a newly supported text file B, a viewer computes [A,B]; if an older replica concurrently republishes the link after removing B, it can write [A] with no snapshotVersion, which is byte-for-byte equal to readSnapshots. This predicate therefore still matches and restores B, re-authorizing a file the owner removed. Include the read publication revision (updatedAt, messages/target revision, or equivalent) in the compare-and-set rather than relying solely on an array that a legitimate republish can reproduce.

AGENTS.md reference: AGENTS.md:L45-L47

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 659772d: the backfill compare-and-set now also requires the updatedAt the viewer read, which every republish bumps and the backfill itself never changes (timestamps: false). share.test.ts 'keeps a legacy replica republish that writes back the snapshot the viewer read' fails without the fix and passes with it.

Comment on lines +91 to +93
return documentParserMimeTypes.some((pattern) => pattern.test(route.mimeType))
? UPLOAD_FALLBACK_TEXT_PLANS.documentParser
: null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Normalize the MIME type before selecting fallback parsing

When textFallbackWithoutTools handles a document reported as application/octet-stream, application/zip, or another alias—common for DOCX/PDF uploads—this test uses the raw Multer MIME and returns no parser plan even though parseDocument supports routing the same file by its extension. The primary Context path already calls resolveUploadMimeType, but the fallback path passes file.mimetype directly, so a tool-less turn silently loses otherwise extractable fallback text; select the plan using resolveEffectiveMimeType(file.originalname, file.mimetype) or pass the already resolved type.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c55cdda: the fallback plan now uses resolveEffectiveMimeType(originalname, mimetype), the same resolution the upload path uses. Covered by fallback.spec.ts 'plans a document reported under a generic type by its resolved type'.

Comment on lines 21 to 23
const uploadedFile = await openai.files.create({
file: fs.createReadStream(file.path),
file: new File([await fs.openAsBlob(file.path)], file.originalname),
purpose: isImage ? FilePurpose.Vision : FilePurpose.Assistants,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Move streamed upload construction into packages/api

This change implements the new filesystem-to-file-backed-Blob upload behavior directly in legacy /api CJS rather than leaving that layer as wiring. Move the uploadable construction and provider invocation behind a TypeScript service in packages/api, with this file limited to importing and calling it; otherwise backend upload behavior continues to accumulate in the legacy boundary that repository guidance explicitly prohibits extending.

AGENTS.md reference: AGENTS.md:L73-L80

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f342ce2: openNamedUpload in packages/api/src/files/upload/disk.ts builds the file-backed upload; OpenAI/crud.js only requires and calls it (covered by disk.spec.ts and OpenAI/crud.spec.js).

@codegraph-librechat codegraph-librechat Bot added the 🗺️ Server Core codegraph: the taxonomy area this belongs to (classifier, confidence ≥ 0.9) label Oct 3, 2026
Adds server-side local document extraction with AnyDoc and pdf-inspector behind the
existing document_parser strategy, keeping configured OCR services as a separate
fallback layer.
dev landed its own share-snapshot delivery marker (`llmDeliveryPath`) and a
share-scoped extracted-text preview, so this branch's parallel `hasTextPreview`
marker is gone: snapshots carry the base's marker, the message preview dialog is
the base's, and the branch keeps only the snapshot-version rebuild that repairs
links snapshotted before text records were captured. A rebuild now also backfills
delivery markers in the same view instead of leaving them to the next one.

The two upload gates (multer's streaming filter and `filterFile`) asked the same
"endpoint allowlist or documentParser allowlist" question in two places; they now
call `isAdmissibleUploadType` in packages/api, each naming its own parser scope.

A CSV left to tools is read as its own text again: the parser's list names
delimited text so a context upload is admissible, which had started routing the
fallback-text plan through the parser and storing a markdown table.
…pe as itself

The process-wide parser limiter was replaced whenever a caller resolved different
bounds. A fresh limiter starts with an active count of zero while the children the
old one admitted are still running, so two requests with different
`maxConcurrentParsers` could run more parses than either bound allows. The bounds
are now fixed at the first parse, a later request naming different ones is logged
once instead of honored, and the spec pins that a wider bound still waits behind
the limiter already admitting.

Admission accepts a type an operator named in `documentParser.supportedMimeTypes`,
so dispatch can be handed a document no engine claims (a vendor MIME under a
filename with no recognizable extension). AnyDoc threw a bare Error for that, which
the upload route reported as a generic 500; it is now
`UnsupportedDocumentTypeError` with code `UNSUPPORTED_DOCUMENT_TYPE` and status
415, recognized by `isDocumentParserRefusal` and named in the user-facing message
table, like every other parser refusal.
Uploads now stage under a per-request path (`<file_id>-<name>`) so two concurrent
uploads of the same filename cannot share one temp file. Three adapters derived the
name they send from that staged path instead of the upload, so the id leaked into
places a user reads it:

- the vector store recorded `<uuid>-report.csv`, which is the name file_search
  citations show (this is what the `Upload for File Search` e2e provisioning test
  caught);
- the configured RAG `/text` extraction announced the same staged basename;
- the OpenAI assistants upload let the SDK infer the name from the stream path, so
  the provider's own file list showed it.

Each now passes `file.originalname` explicitly. The Mistral OCR and code-environment
adapters already did.
filterFile scoped parser-only MIME types to tool_resource context, but routing still promotes the legacy ocr resource to context, so a narrowed endpoint refused an ocr upload the parser would have read. Admission now takes the tool resource and treats ocr as the context path.
The fallback never runs the scan classifier, so a page with text beside an image of more text read as complete. The result now carries mayOmitContent, matching the native path when its classifier cannot run.
The server accepts file_search for every type the document parser reads, but the chooser gated it on retrievalMimeTypes only, so legacy Word, EPUB, PowerPoint and ODP showed no destination with context off. The prose formats are split into proseDocumentMimeTypes and the chooser accepts them for file search.
Also truncates the dialog title through an inner block span, since the title element itself does not clip.
The process-wide parser limiter took its bounds from whichever caller parsed first. A code-artifact preview or fallback-text parse passes no options, so it fixed the defaults and a later upload's documentParser bounds were logged and ignored. A caller with no options no longer fixes the bounds; the first caller that names them resizes the one limiter in place, keeping its running count so the swap never admits more than either bound. The limiter gains resize, and release now checks the bound so a narrowed limiter does not start queued work early.
Dev replaced the attach buttons with the Attach and tools palette, so the document scenarios waited for #attach-file-menu-button and #attach-file-button that no longer render. The helpers now open the palette and pick the source row, or the Upload as Text destination under More upload options.
@berry-13
berry-13 force-pushed the ocr/pdf-inspector-evaluation branch from f342ce2 to 374d0ce Compare October 4, 2026 13:34
@codegraph-librechat codegraph-librechat Bot added 🗺️ Platform Security codegraph: the taxonomy area this belongs to (classifier, confidence ≥ 0.9) 🗺️ Core Infra & Sec codegraph: the taxonomy area this belongs to (classifier, confidence ≥ 0.9) labels Oct 9, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🗺️ Content And Files codegraph: the taxonomy area this belongs to (classifier, confidence ≥ 0.9) 🗺️ Core Infra & Sec codegraph: the taxonomy area this belongs to (classifier, confidence ≥ 0.9) 🗺️ File Storage codegraph: the taxonomy area this belongs to (classifier, confidence ≥ 0.9) 🗺️ Platform Security codegraph: the taxonomy area this belongs to (classifier, confidence ≥ 0.9) 🗺️ Server Core codegraph: the taxonomy area this belongs to (classifier, confidence ≥ 0.9)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants