feat(assets): PDF page rendering via raw: true#356
Conversation
When vault_read_asset is called with raw: true on a PDF, each page is rendered as an image via unpdf's renderPageAsImage + @napi-rs/canvas (prebuilt Skia, no system deps) and returned as MCP image content blocks. The model sees actual page layout, formatting, diagrams, and tables that text extraction loses. Scanned/image-only PDFs that error on text extraction work in raw mode. - Add @napi-rs/canvas dependency (prebuilt Skia binary, ~40-50MB) - New MAX_PDF_RENDER_PAGES env var (default 5) — per-page byte budget is MAX_IMAGE_OUTPUT_BYTES divided evenly across rendered pages - Extend AssetReadResult with kind: "pages" variant - renderPdfPages: sequential rendering at 2x scale, individual page failures logged and skipped, proxy cleanup via try/finally - Handler maps pages result to metadata text + per-page image blocks - 10 new tests covering page rendering, budget division, cap enforcement, partial failures, proxy cleanup, and option passing - Deploy surfaces: env var in all 7 compose/env files + CLI sync - ARCHITECTURE.md, README.md, DOCKERHUB.md updated Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Sorry @aliasunder, you have reached your weekly rate limit of 500000 diff characters.
Please try again later or upgrade to continue using Sourcery
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
The bare filename was ambiguous — no templates.test.ts exists under src/, only under cli/src/__tests__/. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The agent calling the tool can't see or set MAX_PDF_RENDER_PAGES —
state the actual limit ("Up to 5 pages") instead of the config name.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use config.maxPdfRenderPages instead of hardcoding "5" — the description now reflects the actual configured value. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Missing from the MCP registry manifest env var list. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DoS via invalid limit option — trivy-pr flagged it. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Raw mode only needs the page count, not the full text extraction. proxy.numPages (PDFDocumentProxy getter) provides this directly, avoiding a potentially expensive walk of every page's text items on large PDFs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PR Code Suggestions ✨Explore these optional code suggestions:
|
…er, pin dep - Replace manual conditional type inference with Awaited<ReturnType<...>> for the PDF proxy parameter (named PdfProxy type alias) - Extract 62-line result-formatting callback from vault_read_asset handler into formatAssetReadResult — uses flatMap for page blocks instead of imperative push loop - Pin @napi-rs/canvas to exact version (0.1.100) matching all other deps Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PR uploads used trivy-pr-image / trivy-pr-image-remote, but main uses trivy-published-image / trivy-published-image-remote. The mismatch meant GitHub code scanning couldn't diff the PR against main's baseline — "2 configurations not found" and no visibility into which CVEs are new vs pre-existing. Using the same categories lets the comparison work. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… safety Consolidate decomposed assertions into toMatchObject/toEqual (4 tests), move trailing mock restore into onTestFinished, fix misleading test name, and add MAX_PDF_RENDER_PAGES config tests (default, custom, rejection). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The trivy-pr job scans branch-built images, not published GHCR images.
Using the same category as trivy-published ("trivy-published-image")
would label branch scan results as "published image" in the Security
tab — misleading and colliding with the actual published image results.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Phase 4 bug checker reverted the category alignment, reasoning that scanning different images warrants distinct categories. But GitHub code scanning requires matching categories to compute PR-vs-main diffs — the mismatch was the root cause of the "2 configurations not found" warning the user saw in the PR's Code scanning results tab. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Guard against division by zero when totalPages is 0 (empty/malformed PDF) - Pass pagesToRender directly to renderPdfPages instead of re-deriving it from totalPages + maxPages (eliminates divergence risk) Addresses umm-actually review threads on PR #356. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@CodeRabbit review |
✅ Action performedReview finished.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (20)
📝 WalkthroughWalkthroughAdds configurable PDF ChangesPDF raw rendering
CI maintenance
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
getDocumentProxy() without a CanvasFactory leaves pdfjs-dist's internal
NodeCanvasFactory wired to a stub that throws "@napi-rs/canvas is not
available in this environment." The main canvas (created by unpdf's own
factory) works, but pages needing intermediate canvases for transparency
groups, pattern fills, or image masks hit the stub and fail — producing
per-page failures that look intermittent.
Passing raw Uint8Array instead of the proxy lets renderPageAsImage call
getDocumentProxy(data, { CanvasFactory }) internally, wiring both the
main and internal canvas factories to @napi-rs/canvas.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
vault_read_assetis called withraw: trueon a PDF, each page is rendered as an image via unpdf'srenderPageAsImage+@napi-rs/canvas(prebuilt Skia, no system deps) and returned as MCP image content blocksMAX_PDF_RENDER_PAGESenv var (default 5) — per-page byte budget isMAX_IMAGE_OUTPUT_BYTESdivided evenly across rendered pagesChanges
@napi-rs/canvasadded (^0.1.69, ~40-50MB prebuilt Skia binary)maxPdfRenderPagesviaMAX_PDF_RENDER_PAGESenv var,requireNonZeroBytes→requireNonZerorenamerenderPdfPagesfunction (sequential rendering at 2× scale, per-pagefitImageToByteBudget, individual page failures logged and skipped),AssetReadResultextended withkind: "pages"variantkind: "pages"to metadata text block + per-page image/text block pairsraw: truepage rendering, new error entry,rawparam description updated, returns section updatedTest plan
npm run build— TypeScript compiles cleannpm run lint— 0 errorsnpm test— 2027 tests pass (10 new)test_deploy.yml→ live verification with a real PDF (raw: true+raw: false)🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
Bug Fixes