feat(index): multimodal PDF ingest behind opt-in flag (bo-j5r0)#41
Merged
Conversation
Adds an opt-in `[index] multimodal` flag. When enabled, `bobbin index` also walks `**/*.pdf`, extracts text via the pure-Rust `pdf-extract` crate (no Python / native toolchain), and chunks it like a plain-text document tagged `language = "pdf"` — making runbooks, design docs, and specs searchable. Off by default, so the default dependency/behavior profile is unchanged. PDFs that yield no text (image-only / encrypted) are skipped like empty files. Image captioning (vision LLM) is intentionally out of scope: bobbin has no chat/vision provider path yet. `src/index/multimodal.rs` is the seam where that second extractor will slot in; tracked as a follow-up bead. - src/index/multimodal.rs: extractor (is_multimodal_file, extract_text) + tests - cli/index.rs: read_indexable_content helper at both read sites; auto-inject `**/*.pdf` into the walk when the flag is set - config.rs: IndexConfig.multimodal (default false) - parser.rs: detect_language recognizes .pdf -> line-chunked "pdf" - tests/fixtures/sample.pdf: minimal valid PDF for extraction test - docs + CHANGELOG Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CG47xMt5cR8EQGk26Ze64u
Vale Style check flagged 'multimodal'/'Multimodal', 'PDFs', and 'runbooks' in docs/book/src/config/index.md as spelling errors. Add them to the Bobbin vocabulary accept-list (proper terms, not bypasses): [Mm]ultimodal covers both cases per the existing [Hh]eatmap idiom. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H7b5eTqAZrisQN6NJ4Kn2z
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Implements the PDF half of bo-j5r0 (multimodal ingest, aegis-1p0 Gap 2 / graphify parity). Opt-in
[index] multimodalflag makesbobbin indexalso walk**/*.pdf, extract text via the pure-Rustpdf-extractcrate, and chunk it like a plain-text doc (language = "pdf") — so runbooks, design PDFs, and specs become searchable and graph-extractable.Why scoped to PDF
The handoff explicitly authorized splitting: "PDF (simpler, pure-Rust) from image-vision (needs LLM)… PDF alone may satisfy the core AC." Bobbin has no chat/vision provider path today (only ONNX/OpenAI-compatible embeddings), so image captioning requires a whole new LLM subsystem.
src/index/multimodal.rsis the seam where that second extractor slots in — tracked as a follow-up bead.AC status
bobbin indexpicks up.pdfbehind a config flagDesign
**/*.pdfinto the walk (no manualincludeedit).read_indexable_contenthelper routes both index read sites (hash-check + parse); non-PDF path is byte-for-byte the oldread_to_string.Tests
cargo test --bin bobbin: 894 passed. New: PDF extraction from fixture, extension detection (case-insensitive), unsupported-ext rejection, flag-gates-walk on/off. Clippy clean (no new warnings).🤖 Generated with Claude Code