Skip to content

feat(extraction): add progressive LiteParse PDF processing#627

Merged
urjitc merged 4 commits into
mainfrom
codex/liteparse-progressive-pdf-extraction
Jul 11, 2026
Merged

feat(extraction): add progressive LiteParse PDF processing#627
urjitc merged 4 commits into
mainfrom
codex/liteparse-progressive-pdf-extraction

Conversation

@urjitc

@urjitc urjitc commented Jul 11, 2026

Copy link
Copy Markdown
Member

Summary

  • add a pinned LiteParse container as the fast first pass for PDF extraction
  • publish per-page Markdown immediately, then atomically replace it with the existing LlamaParse result
  • preserve a usable LiteParse projection when enhancement fails
  • reject password-protected and invalid PDFs before writing them to workspace storage
  • add structured stage-level extraction telemetry

Why

PDF uploads previously remained unavailable until the full LlamaParse job completed, and password-protected PDFs were accepted even though neither the viewer nor extraction pipeline could use them. This provides a fast usable projection while preserving the higher-quality enhancement path, and moves authoritative PDF readability validation ahead of persistence.

Changes

  • adds a Cloudflare LiteParsePdfExtractor container using pinned @llamaindex/liteparse@2.5.1
  • configures standard-2 instances with a maximum of two and no internet access
  • keeps LiteParse OCR disabled for the latency-oriented provisional pass; LlamaParse remains the quality/OCR enhancement
  • records one canonical workflow outcome with LiteParse and enhancement timing, page counts, output length, provider, and error fields
  • adds unpdf@1.6.2 validation at the prepared-upload boundary so both direct and converted PDFs are checked before R2 writes
  • returns explicit PASSWORD_PROTECTED_PDF and INVALID_PDF 422 errors

Testing

  • pnpm check
  • pnpm test — 23 tests passed
  • pnpm build
  • LiteParse Docker image build
  • Cloudflare staging deploy dry run with the new container and binding
  • manual smoke test against a real PDF
  • local unpdf verification against plain and AES-256 password-protected PDFs

Review Notes

Start with workspace-file-extraction-workflow.ts for orchestration, liteparse-projection.ts for the provisional publish boundary, and pdf-upload-validation.ts for upload rejection behavior.

The local Node runtime was 25.8.0 while the repository requests Node 24.11.x. Checks passed despite the engine warning. pnpm peers check also reports the repository's existing Babel, Vite, and Tailwind peer mismatches.


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added faster PDF processing that produces searchable Markdown page content.
    • Added validation for uploaded PDFs, with clear errors for invalid or password-protected files.
    • Workspaces can now become available using LiteParse results when standard extraction is delayed or partially fails.
  • Bug Fixes
    • Improved PDF error classification and cleanup during upload validation.
  • Observability
    • Added extraction timing, page count, content size, and outcome reporting for improved monitoring.

@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@urjitc, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 37 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d6391c87-9d5f-47a1-9c95-70c0c3d5fcb3

📥 Commits

Reviewing files that changed from the base of the PR and between b0f03b9 and 9cb460c.

📒 Files selected for processing (10)
  • containers/liteparse/Dockerfile
  • containers/liteparse/server.mjs
  • src/features/workspaces/extraction/liteparse-projection.ts
  • src/features/workspaces/extraction/providers/liteparse-response.ts
  • src/features/workspaces/extraction/providers/liteparse.test.ts
  • src/features/workspaces/extraction/providers/liteparse.ts
  • src/features/workspaces/extraction/workspace-file-extraction-observability.ts
  • src/features/workspaces/extraction/workspace-file-extraction-workflow.ts
  • src/features/workspaces/upload/workspace-file-upload-normalization.test.ts
  • src/features/workspaces/upload/workspace-file-upload-normalization.ts
📝 Walkthrough

Walkthrough

Adds PDF readability validation, a LiteParse container and extraction provider, provisional markdown projections, workflow fallback handling, and telemetry for LiteParse and enhancement outcomes.

Changes

LiteParse PDF flow

Layer / File(s) Summary
PDF upload validation
package.json, src/features/workspaces/model/workspace-file/policy.ts, src/features/workspaces/upload/*
Prepared PDF uploads are opened with unpdf; password-protected and invalid documents produce typed 422 upload errors, with coverage for both cases.
LiteParse container deployment
containers/liteparse/*, src/server.ts, wrangler.jsonc, worker-configuration.d.ts
Adds the LiteParse HTTP container, Durable Object binding, environment configuration, migration, and generated environment types.
LiteParse extraction and projection
src/features/workspaces/extraction/providers/*, src/features/workspaces/extraction/liteparse-projection.ts
Extracts PDF pages through the pooled container, validates markdown responses, and upserts provisional workspace projections with source metadata.
Workflow fallback outcomes
src/features/workspaces/extraction/workspace-file-extraction-workflow.ts
Runs LiteParse with extraction, records stage results, and returns a ready partial result from LiteParse when the primary extraction path fails.
Extraction telemetry contracts
src/features/workspaces/extraction/types.ts, src/features/workspaces/extraction/workspace-file-extraction-observability.ts, src/integrations/posthog/events.ts
Records LiteParse and enhancement outcomes, durations, errors, markdown length, and page counts in operational and PostHog telemetry.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WorkspaceFileUpload
  participant WorkspaceFileExtractionWorkflow
  participant LiteParsePdfExtractor
  participant WorkspaceKernel
  WorkspaceFileUpload->>WorkspaceFileUpload: validate PDF readability
  WorkspaceFileExtractionWorkflow->>LiteParsePdfExtractor: submit PDF bytes
  LiteParsePdfExtractor-->>WorkspaceFileExtractionWorkflow: return markdown pages
  WorkspaceFileExtractionWorkflow->>WorkspaceKernel: upsert provisional projection
  WorkspaceFileExtractionWorkflow-->>WorkspaceFileExtractionWorkflow: record extraction outcome
Loading

Suggested labels: capy

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: progressive LiteParse-based PDF processing.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/liteparse-progressive-pdf-extraction

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@urjitc
urjitc marked this pull request as ready for review July 11, 2026 23:23
@greptile-apps

greptile-apps Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a faster staged PDF extraction path before the existing enhancement flow. The main changes are:

  • A LiteParse container and worker binding for provisional PDF parsing.
  • Provisional per-page Markdown publishing before the LlamaParse enhancement step.
  • Partial extraction telemetry when enhancement fails after LiteParse succeeds.
  • Upload-time PDF readability checks with explicit invalid and password-protected errors.

Confidence Score: 4/5

The converted PDF upload path needs a buffer-copy fix before merging.

Converted PDFs validate the same buffer that is later written to storage. Direct PDF uploads and the LiteParse extraction flow otherwise follow the changed contracts shown in the diff. Container and environment bindings are added consistently across the changed configuration sections.

src/features/workspaces/upload/workspace-file-upload-normalization.ts

T-Rex T-Rex Logs

What T-Rex did

  • I ran a focused Vitest harness that exercises the prepareWorkspaceFileUpload path through the office_to_pdf converter with a real valid PDF ArrayBuffer and unpdf validation, and observed the converter produced a 327-byte PDF starting with %PDF before validation.
  • After validation, the prepared.body remained the same ArrayBuffer object but its byteLength was 0 and reading the first bytes threw a detached ArrayBuffer error, and a simulated later persistence/write also threw a detached ArrayBuffer error.
  • I reviewed the test run and logs: the targeted Vitest run reported 2 test files and 9 tests passed, and the PDF validation runtime harness reported ACCEPTED for plain-minimal-valid-pdf and REJECTED for invalid-byte-blob and password-protected-pdf, with EXIT_CODE 0.
  • I confirmed supporting container tooling status: LiteParse container import succeeded (EXIT_CODE: 0) and a Docker blocker was noted (docker: command not found).

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
src/features/workspaces/extraction/workspace-file-extraction-workflow.ts Adds the LiteParse stage before the existing extraction step and records success, partial, and error outcomes.
src/features/workspaces/extraction/liteparse-projection.ts Publishes a provisional pages projection from LiteParse for PDF workspace files.
src/features/workspaces/extraction/providers/liteparse.ts Adds the Cloudflare container client and validates the LiteParse response before returning pages.
containers/liteparse/server.mjs Adds the container HTTP server that accepts a PDF file and returns per-page Markdown.
src/features/workspaces/upload/pdf-upload-validation.ts Adds unpdf-based validation and maps unreadable PDFs to upload errors.
src/features/workspaces/upload/workspace-file-upload-normalization.ts Validates prepared PDF uploads before persistence, with a buffer ownership issue on converted PDFs.
src/features/workspaces/model/workspace-file/policy.ts Extends upload errors with new PDF-specific codes and 422 status support.
wrangler.jsonc Registers the LiteParse container, durable object binding, and migration across environments.

Fix All in Cursor

Reviews (1): Last reviewed commit: "fix(upload): reject unreadable PDFs befo..." | Re-trigger Greptile

if (input.descriptor.assetKind !== "pdf") {
return;
}

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.

P1 Converted Buffer Can Be Consumed

When a converter returns a PDF as an ArrayBuffer, this line validates the same buffer that the caller later persists as prepared.body. If the PDF parser takes ownership of or transfers that buffer while loading the document, validation can pass but the later R2 write receives a detached or consumed buffer, so converted PDF uploads can fail or be stored incorrectly.

Artifacts

Repro: focused Vitest harness for converted PDF ArrayBuffer consumption

  • Contains supporting evidence from the run (text/typescript; charset=utf-8).

Repro: Vitest config used to run the artifact harness

  • Contains supporting evidence from the run (text/typescript; charset=utf-8).

Repro: verbose failing test output showing detached prepared.body and failed persistence simulation

  • Keeps the command output available without making the summary code-heavy.

View artifacts

T-Rex Ran code and verified through T-Rex

Fix in Cursor

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

ℹ️ 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 containers/liteparse/server.mjs Outdated
inputBytes = bytes.byteLength;
const result = await parser.parse(bytes);
const pages = result.pages.map((page) => ({
markdown: page.markdown ?? page.text ?? "",

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 Use LiteParse's rendered Markdown output

For PDFs where the LiteParse pass succeeds but enhancement later fails, this retains the provisional projection. The LiteParse Node API documents Markdown output as being returned on result.text, while result.pages[] exposes page text/items rather than a markdown field (https://developers.llamaindex.ai/liteparse/guides/library-usage/#markdown-output); this fallback therefore stores layout-preserved/plain page text in the projection, dropping the headings/tables/link/image Markdown that the fast projection is supposed to provide.

Useful? React with 👍 / 👎.

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
containers/liteparse/server.mjs (1)

43-50: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add a timeout to parser.parse() to prevent indefinite hangs.

If LiteParse hangs on a malformed PDF, the parse promise remains pending even after the upstream workflow step times out and closes the fetch connection. With max_instances: 2, accumulated hung operations could degrade container availability. Wrapping with Promise.race against a timeout ensures the handler completes, though the underlying parse may still run in the background.

♻️ Suggested timeout wrapper
 	const bytes = new Uint8Array(await file.arrayBuffer());
 	inputBytes = bytes.byteLength;
-	const result = await parser.parse(bytes);
+	const parseTimeout = new Promise<never>((_, reject) => {
+		setTimeout(() => reject(new Error("Parse timeout")), 90_000);
+	});
+	const result = await Promise.race([parser.parse(bytes), parseTimeout]);
 	const pages = result.pages.map((page) => ({
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@containers/liteparse/server.mjs` around lines 43 - 50, Wrap the
parser.parse(bytes) promise in the request handler with a Promise.race timeout
so malformed PDFs cannot leave the handler pending indefinitely. Preserve the
existing pages mapping and successful response flow, and make the timeout reject
or otherwise follow the handler’s existing error path after the configured
duration.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@containers/liteparse/Dockerfile`:
- Around line 1-12: Update the Dockerfile to run the container as the
image-provided non-root node user by adding USER node after copying the
application files and before EXPOSE/CMD runtime instructions. Do not change the
existing dependency installation or startup commands.

---

Nitpick comments:
In `@containers/liteparse/server.mjs`:
- Around line 43-50: Wrap the parser.parse(bytes) promise in the request handler
with a Promise.race timeout so malformed PDFs cannot leave the handler pending
indefinitely. Preserve the existing pages mapping and successful response flow,
and make the timeout reject or otherwise follow the handler’s existing error
path after the configured duration.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5c713201-75bb-4a4b-a009-63d114267e17

📥 Commits

Reviewing files that changed from the base of the PR and between 0a24e28 and b0f03b9.

⛔ Files ignored due to path filters (2)
  • containers/liteparse/package-lock.json is excluded by !**/package-lock.json
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (19)
  • containers/liteparse/Dockerfile
  • containers/liteparse/package.json
  • containers/liteparse/server.mjs
  • package.json
  • src/features/workspaces/extraction/liteparse-projection.ts
  • src/features/workspaces/extraction/providers/liteparse-response.ts
  • src/features/workspaces/extraction/providers/liteparse.test.ts
  • src/features/workspaces/extraction/providers/liteparse.ts
  • src/features/workspaces/extraction/types.ts
  • src/features/workspaces/extraction/workspace-file-extraction-observability.ts
  • src/features/workspaces/extraction/workspace-file-extraction-workflow.ts
  • src/features/workspaces/model/workspace-file/policy.ts
  • src/features/workspaces/upload/pdf-upload-validation.test.ts
  • src/features/workspaces/upload/pdf-upload-validation.ts
  • src/features/workspaces/upload/workspace-file-upload-normalization.ts
  • src/integrations/posthog/events.ts
  • src/server.ts
  • worker-configuration.d.ts
  • wrangler.jsonc

Comment thread containers/liteparse/Dockerfile

@cubic-dev-ai cubic-dev-ai Bot 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.

All reported issues were addressed across 21 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/features/workspaces/upload/workspace-file-upload-normalization.ts Outdated
Comment thread src/features/workspaces/extraction/liteparse-projection.ts
Comment thread src/features/workspaces/extraction/providers/liteparse.ts Outdated
Comment thread src/features/workspaces/extraction/workspace-file-extraction-observability.ts Outdated
Comment thread src/features/workspaces/extraction/providers/liteparse-response.ts Outdated
Comment thread containers/liteparse/server.mjs Outdated
Comment thread containers/liteparse/Dockerfile
Use rendered LiteParse markdown directly.
Preserve converted PDF upload buffers.
Keep successful extraction writes from falling back.

Co-authored-by: Cursor <cursoragent@cursor.com>
@capy-ai

capy-ai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Capy auto-review is paused for this organization because the usage-cycle auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

@cubic-dev-ai cubic-dev-ai Bot 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.

1 issue found across 10 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/features/workspaces/extraction/workspace-file-extraction-workflow.ts">

<violation number="1" location="src/features/workspaces/extraction/workspace-file-extraction-workflow.ts:222">
P2: A failed artifact deletion is now converted into a successful terminal step, so transient R2 failures permanently accumulate per-workflow artifacts and are invisible to telemetry. Preserve the successful extraction result, but schedule/retry cleanup or at least emit an operational failure for this branch.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

await this.env.WORKSPACE_KERNEL_FILES.delete(extraction.artifactKey);
return { deleted: extraction.artifactKey };
} catch (error) {
return {

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.

P2: A failed artifact deletion is now converted into a successful terminal step, so transient R2 failures permanently accumulate per-workflow artifacts and are invisible to telemetry. Preserve the successful extraction result, but schedule/retry cleanup or at least emit an operational failure for this branch.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/features/workspaces/extraction/workspace-file-extraction-workflow.ts, line 222:

<comment>A failed artifact deletion is now converted into a successful terminal step, so transient R2 failures permanently accumulate per-workflow artifacts and are invisible to telemetry. Preserve the successful extraction result, but schedule/retry cleanup or at least emit an operational failure for this branch.</comment>

<file context>
@@ -235,6 +213,41 @@ export class WorkspaceFileExtractionWorkflow extends WorkflowEntrypoint<
+				await this.env.WORKSPACE_KERNEL_FILES.delete(extraction.artifactKey);
+				return { deleted: extraction.artifactKey };
+			} catch (error) {
+				return {
+					deleted: null,
+					errorType: error instanceof Error ? error.name : "UnknownError",
</file context>

Use LiteParse's per-page markdown contract.
Keep document text out of the projection protocol.

Co-authored-by: Cursor <cursoragent@cursor.com>

@cubic-dev-ai cubic-dev-ai Bot 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.

0 issues found across 4 files (changes from recent commits).

Requires human review: Auto-approval blocked by 1 unresolved issue from previous reviews.

Re-trigger cubic

@urjitc
urjitc merged commit 6632022 into main Jul 11, 2026
10 checks passed
@github-project-automation github-project-automation Bot moved this from Backlog to Done in Dev Board Jul 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant