Skip to content

[codex] Add PDF file upload ingest - #24

Merged
tomnguyen103 merged 3 commits into
mainfrom
codex/pdf-file-upload-ingest
Jun 6, 2026
Merged

[codex] Add PDF file upload ingest#24
tomnguyen103 merged 3 commits into
mainfrom
codex/pdf-file-upload-ingest

Conversation

@tomnguyen103

@tomnguyen103 tomnguyen103 commented Jun 6, 2026

Copy link
Copy Markdown
Owner

What changed

  • Added multipart POST /ingest/upload for .pdf, .txt, and .md uploads, including local PDF text extraction with pypdf.
  • Added file_upload source type support, migration 0006, upload parser tests, API auth/config coverage, and frontend upload mode with selected-file handling.
  • Fixed PDF Q&A grounding by adding strict-fulltext fallback retrieval, block-aware citation validation, cross-language cited-context handling, and original-query-first Agentic RAG planning.
  • Documented upload usage, encrypted-PDF behavior, progress, and implementation trade-offs.

Why

Users could select pdf_upload metadata but had no actual multipart upload path. After upload worked, PDF questions still failed in chat when strict full-text missed typo-adjacent queries or citation validation rejected translated English answers over Vietnamese PDF chunks.

Validation

  • SECOND_BRAIN_TEST_DATABASE_URL=postgresql+psycopg://second_brain:second_brain@localhost:5433/second_brain SECOND_BRAIN_LLM_PROVIDER=fake SECOND_BRAIN_AGENTIC_RAG_ENABLED=false pytest -> 279 passed, 8 warnings
  • npm run lint
  • npm run build (passes; existing Next multiple-lockfile warning remains)
  • npm audit --audit-level=high
  • python -m pip check
  • docker compose config --quiet
  • git diff --cached --check
  • Live /ingest/upload smoke accepted an empty-password encrypted PDF and smoke source was deleted
  • Live /chat probes passed twice for normal RAG and twice for Agentic RAG on the uploaded PDF question

Notes

  • Original uploaded binaries are not retained; only extracted text plus parser/original-filename metadata is stored.
  • Permission-encrypted PDFs are accepted when empty-password decrypt works; password-required PDFs are rejected.

Summary by CodeRabbit

  • New Features

    • Added file upload API with support for PDF, text, and markdown files
    • New upload interface featuring multi-file selection and optional metadata tagging
    • Keyword-based search fallback for retrieving uploaded file titles and content
    • Enhanced citation validation to support multilingual source material
  • Bug Fixes

    • Original user questions now preserved in agentic query planning
    • Improved citation repair with detailed failure diagnostics

@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown

Too much diff to scan? Review this PR in Change Stack to start with the highest-impact changes.

Review Change Stack

Warning

Review limit reached

@tomnguyen103, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 1 minute and 26 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

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.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8616321d-d6e9-4f2e-b95b-96ceb58085b7

📥 Commits

Reviewing files that changed from the base of the PR and between 9ed4c9e and ef219cd.

📒 Files selected for processing (5)
  • backend/app/chat/service.py
  • backend/tests/helpers.py
  • backend/tests/integration/test_ingest.py
  • backend/tests/unit/test_upload_parsers.py
  • docs/data-model/er-diagram.md
📝 Walkthrough

Walkthrough

This PR introduces a complete multipart file upload feature for the ingest API (POST /ingest/upload), adds upload file parsing for PDFs and text, enhances citation validation with block-awareness and cross-lingual support, implements keyword-fallback retrieval for uploaded sources, preserves original questions in query planning, and updates the frontend with a text/upload ingest mode toggle.

Changes

File Upload Ingestion Flow

Layer / File(s) Summary
Configuration, schema, and type contracts
backend/app/config.py, backend/app/db/models.py, backend/migrations/versions/0006_*.py, frontend/lib/api/types.ts
Added Settings fields for upload limits and extension allowlist; extended Source.type constraint to include file_upload and pdf_upload; created migration to enforce new constraint; updated frontend SourceType union.
Upload file parsing and validation
backend/app/ingest/parsers.py, backend/tests/unit/test_upload_parsers.py
Implemented parse_upload_bytes dispatcher, _parse_pdf with header/encryption validation and per-page text extraction, _parse_text with UTF-8 and binary rejection, plus filename sanitization and metadata capture. Comprehensive unit tests cover PDF encryption handling, text parsing, and edge cases.
Ingest upload API endpoint and helpers
backend/app/api/ingest.py, backend/tests/integration/test_ingest.py, backend/tests/unit/test_api_auth.py
Added POST /ingest/upload multipart endpoint with file validation, per-file parsing, metadata merge, tag propagation, source typing. Refactored existing /ingest to use centralized response formatter. Integration tests verify text/PDF uploads, tag propagation, and validation error responses; auth tests confirm token requirement.

Retrieval and Citation Improvements

Layer / File(s) Summary
Enhanced citation validation and repair
backend/app/chat/service.py, backend/tests/unit/test_chat_citation_support.py
Splits answers into blocks, detects structural segments (headers, list prefixes), adds cross-lingual detection via non-ASCII content, computes citation support with adaptive overlap rules, populates repair prompts with specific failed segments and invalid markers. Tests verify block citations, structural skipping, cross-language handling.
Hybrid search keyword fallback
backend/app/retrieval/hybrid.py, backend/tests/integration/test_retrieval.py
Extracts up to 8 keyword terms from queries, implements substring-match SQL fallback restricted to pdf_upload and file_upload sources, uses fallback only when strict full-text yields zero results, extends metadata with fallback flags and candidate counts. Tests verify fallback activation for uploads, non-activation for manual sources.

Query Planning and Frontend

Layer / File(s) Summary
Query plan with original question preservation
backend/app/agentic_rag/service.py, backend/tests/unit/test_agentic_rag.py
Modified parse_query_plan fallback to always include original question as first query element before planner paraphrases, clamped to max-query limit. Tests confirm original question precedence and query count clamping.
Frontend ingest UI with upload mode
frontend/app/ingest/page.tsx, frontend/lib/api/client.ts
Added text/upload mode toggle; mode-dependent source type options and form validity (docs for text, files for upload). Conditionally renders documents editor or file-picker UI. Updated API client to detect FormData and skip JSON content-type header. Tests verify authentication gate.

Tests and Documentation

Layer / File(s) Summary
Upload parsing and endpoint tests
backend/tests/unit/test_upload_parsers.py, backend/tests/integration/test_ingest.py, backend/tests/unit/test_config.py
Unit tests for PDF/text parsing with encryption, metadata extraction, filename sanitization. Integration tests verify text/PDF uploads, tag propagation, and validation error messages. Config tests check upload settings defaults and environment overrides.
Citation and retrieval tests
backend/tests/unit/test_chat_citation_support.py, backend/tests/integration/test_retrieval.py
Unit tests for block citation support, structural heading handling, and cross-language context. Integration tests verify keyword fallback activation on uploaded sources and non-activation on manual sources with weak_context flag.
Progress and usage documentation
docs/PROGRESS.md, docs/USAGE.md, docs/data-model/er-diagram.md, docs/implementation-notes.md
Documented new upload endpoint, supported file types, PDF encryption handling, retrieval fallback behavior, citation improvements, and implementation details.

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • tomnguyen103/second-brain#23: Overlapping changes in agentic RAG query planning and chat citation validation/repair; this PR refines and builds upon those foundations.

Poem

🐰 With whiskers twitched and paws at play,
Files hop through upload gates today,
PDFs parsed, marked with care—
Search keywords fall through upload air.
Citations bloom in colors bright,
Cross-tongue talks see clearer light! 🌈

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.55% which is insufficient. The required threshold is 35.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title '[codex] Add PDF file upload ingest' directly describes the main change: adding a PDF file upload feature to the ingest endpoint, which aligns with the primary objective of adding multipart POST /ingest/upload supporting .pdf, .txt, and .md uploads.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/pdf-file-upload-ingest

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

@tomnguyen103
tomnguyen103 marked this pull request as ready for review June 6, 2026 03:41
@tomnguyen103

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

🧹 Nitpick comments (3)
backend/app/chat/service.py (1)

145-145: 💤 Low value

Consider extracting the structural heading token threshold.

The <= 12 token limit for detecting structural headings is a magic number. While acceptable for this heuristic, extracting it as a named constant (e.g., _STRUCTURAL_HEADING_MAX_TOKENS = 12) would improve maintainability.

🤖 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 `@backend/app/chat/service.py` at line 145, Extract the magic number 12 into a
named module-level constant like _STRUCTURAL_HEADING_MAX_TOKENS = 12 and replace
the literal in the structural heading check (the return in the function using
_support_tokens) with a comparison against that constant (e.g., return
len(_support_tokens(text)) <= _STRUCTURAL_HEADING_MAX_TOKENS) so the threshold
is easy to find and adjust.
docs/data-model/er-diagram.md (1)

29-29: 💤 Low value

Consider alphabetizing the source type enum.

The source type values are currently listed in insertion order rather than alphabetically. While this doesn't affect functionality, consistent alphabetical ordering improves maintainability.

📝 Suggested alphabetical order
-        text        type          "enum: notes_folder|github|rss|pdf_upload|file_upload|bookmark|research_note|manual"
+        text        type          "enum: bookmark|file_upload|github|manual|notes_folder|pdf_upload|research_note|rss"
🤖 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 `@docs/data-model/er-diagram.md` at line 29, The enum listing for the
source/type field is not alphabetized; reorder the enum values in the type
declaration (the string: "enum:
notes_folder|github|rss|pdf_upload|file_upload|bookmark|research_note|manual")
into alphabetical order (by value name) so the enum reads consistently and is
easier to maintain; update the enum string accordingly wherever the type/source
enum is defined in the ER diagram.
backend/tests/unit/test_upload_parsers.py (1)

8-39: 💤 Low value

Consider extracting PDF test helpers to shared test utilities.

The _sample_pdf_bytes() helper is duplicated between test_ingest.py and test_upload_parsers.py. While this duplication is minor for test code, extracting it to a shared test utility module (e.g., tests/helpers.py) would improve maintainability if more tests need to generate test PDFs.

🤖 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 `@backend/tests/unit/test_upload_parsers.py` around lines 8 - 39, The
_sample_pdf_bytes() helper is duplicated; extract it into a shared test utility
(e.g., create a tests.helpers module) by moving the function (rename to
sample_pdf_bytes or keep same name) into that module and then update
test_upload_parsers.py and test_ingest.py to import the helper from
tests.helpers instead of defining it inline; remove the duplicate definitions in
both test files and run tests to ensure imports and references to
_sample_pdf_bytes() (or the chosen name) are updated accordingly.
🤖 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.

Nitpick comments:
In `@backend/app/chat/service.py`:
- Line 145: Extract the magic number 12 into a named module-level constant like
_STRUCTURAL_HEADING_MAX_TOKENS = 12 and replace the literal in the structural
heading check (the return in the function using _support_tokens) with a
comparison against that constant (e.g., return len(_support_tokens(text)) <=
_STRUCTURAL_HEADING_MAX_TOKENS) so the threshold is easy to find and adjust.

In `@backend/tests/unit/test_upload_parsers.py`:
- Around line 8-39: The _sample_pdf_bytes() helper is duplicated; extract it
into a shared test utility (e.g., create a tests.helpers module) by moving the
function (rename to sample_pdf_bytes or keep same name) into that module and
then update test_upload_parsers.py and test_ingest.py to import the helper from
tests.helpers instead of defining it inline; remove the duplicate definitions in
both test files and run tests to ensure imports and references to
_sample_pdf_bytes() (or the chosen name) are updated accordingly.

In `@docs/data-model/er-diagram.md`:
- Line 29: The enum listing for the source/type field is not alphabetized;
reorder the enum values in the type declaration (the string: "enum:
notes_folder|github|rss|pdf_upload|file_upload|bookmark|research_note|manual")
into alphabetical order (by value name) so the enum reads consistently and is
easier to maintain; update the enum string accordingly wherever the type/source
enum is defined in the ER diagram.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: aca293bb-ab54-41b4-abdc-f1ba3addafc3

📥 Commits

Reviewing files that changed from the base of the PR and between 2d6c229 and 9ed4c9e.

📒 Files selected for processing (24)
  • backend/app/agentic_rag/service.py
  • backend/app/api/ingest.py
  • backend/app/chat/service.py
  • backend/app/config.py
  • backend/app/db/models.py
  • backend/app/ingest/parsers.py
  • backend/app/retrieval/hybrid.py
  • backend/migrations/versions/0006_file_upload_source_type.py
  • backend/requirements.prod.txt
  • backend/requirements.txt
  • backend/tests/integration/test_ingest.py
  • backend/tests/integration/test_retrieval.py
  • backend/tests/unit/test_agentic_rag.py
  • backend/tests/unit/test_api_auth.py
  • backend/tests/unit/test_chat_citation_support.py
  • backend/tests/unit/test_config.py
  • backend/tests/unit/test_upload_parsers.py
  • docs/PROGRESS.md
  • docs/USAGE.md
  • docs/data-model/er-diagram.md
  • docs/implementation-notes.md
  • frontend/app/ingest/page.tsx
  • frontend/lib/api/client.ts
  • frontend/lib/api/types.ts

@tomnguyen103

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@tomnguyen103
tomnguyen103 merged commit e2df39d into main Jun 6, 2026
11 checks passed
@tomnguyen103
tomnguyen103 deleted the codex/pdf-file-upload-ingest branch June 6, 2026 04:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant