feat(source): pdf and audio sources with page/timestamp receipts - #720
Merged
Conversation
a spec, a paper, a recorded call could not become citable material. a receipt is the byte span [byte_start, byte_end) into a source's stored bytes, and the bytes of a pdf or an mp3 do not spell the sentence anyone wants to quote — so that knowledge either stayed out of the kb or entered as an unattributable paraphrase. the fix keeps the receipt primitive untouched. `vouch source add spec.pdf` (or call.mp3) extracts the text layer / transcript and stores *that* as the content-addressed artifact, so byte-offset receipts keep verifying by `==` with no new code path — ingest, the receipt gate, kb.source_verify and receipt coverage all work on it unchanged. what is new is a coordinate map on Source.metadata recording which byte range came from which page or which point in the recording, so a verified receipt also resolves to `p7` or `t=00:14:23` in the original. `vouch source locate <id> <quote>` prints it; --raw registers the binary untouched. no new hard dependency, as the issue requires. pypdf is the optional [pdf] extra imported lazily, and transcription is a configured command (sources.transcribe_cmd) following the compile.llm_cmd pattern, so vouch never bundles a speech model. a scanned pdf with no text layer raises rather than registering an empty source — ocr is explicitly out of scope, and knowledge that silently became empty is worse than knowledge that refused to enter. the original's sha256 is recorded, so verify.py re-checks the pdf or the recording for drift. extracting to text by hand severs exactly that link, which is what makes doing it inside vouch worth the module. also carries the capture.py coerce_numeric fix already on the queue: a string min_observations raises ValueError out of capture.load_config on the current test branch and reddens the suite for every pr opened against it. included so this pr's ci is green on its own merits; it is a two-line overlap that resolves cleanly if the other lands first. closes vouchdev#613 Co-authored-by: Cursor <cursoragent@cursor.com>
changelog union only; no source conflict.
Contributor
|
diff coverage: n/a — this PR changes no python under |
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.
closes #613
why
a spec, a paper, a recorded call could not become citable material. a receipt is
the byte span
[byte_start, byte_end)into a source's stored bytes, and thebytes of a pdf or an mp3 do not spell the sentence anyone wants to quote. so that
knowledge either stayed out of the kb entirely or entered as an unattributable
paraphrase — the failure the issue describes.
what
vouch source add spec.pdf(orcall.mp3) extracts the text layer / transcriptand stores that as the content-addressed artifact. byte-offset receipts keep
verifying by
==with no new code path, so ingest, the receipt gate,kb.source_verifyand receipt coverage all work on media sources unchanged.what is new is a coordinate map on
Source.metadata, recording which byterange came from which page or which point in the recording. a verified receipt
therefore also resolves to a real location in the original:
the byte span is still the part that verifies; the
p7/t=00:14:23prefix iswhat makes the citation checkable by a human holding the original.
--rawregisters the binary untouched.
the two hard requirements from the issue
no new hard dependency. pypdf is the optional
[pdf]extra, imported lazilyinside
extract_pdf_pagesand never touched unless someone registers a pdf.audio needs no extra at all: transcription is a configured command
(
sources.transcribe_cmd, following thecompile.llm_cmdpattern), so vouchnever bundles a speech model. the command emits webvtt or subrip; both parse.
fail loudly. a scanned pdf with no text layer raises rather than registering
an empty source, and there is no silent ocr fallback — knowledge that quietly
became empty is worse than knowledge that refused to enter.
the part that touches stored shape
the issue flags the receipt coordinate model as the one delicate piece. the
approach here is deliberately additive:
Evidenceis unchanged, receipts remainbyte offsets, and the map lives in
Source.metadataas plain scalars that diffreadably like everything else under
.vouch/.receipt_for_quotetakes anoptional
coordinatesargument that only enriches the human-facinglocator. amalformed or missing map degrades to "no coordinate", never to an exception.
verify.pynow re-checks the original binary against a recordedorigin_sha256, sovouch source verifystill notices a pdf or a recordingchanging under a claim that cites it. extracting to text by hand severs exactly
that link, which is what makes doing it inside vouch worth the module.
tests
56 tests in
tests/test_media.py, none of which need pypdf installed or aspeech model on PATH — the pdf reader is injected as a fake module and the
transcription command is a shell one-liner, which is the point of both being
optional. covers byte-vs-character offsets on multibyte text, page/timestamp
resolution including the separator gap that belongs to no page, malformed
coordinate maps, webvtt and subrip parsing, the scanned-pdf refusal, the missing
extra, command failure and timeout, drift detection against the original, and
the cli surface.
pytest/mypy src/ruff check src testsgreen, diff coverage 100%.one thing to flag
this branch also carries the two-line
capture.pycoerce_numericfix that isalready on the queue in #693. a string
min_observationsraisesValueErroroutof
capture.load_configon the currenttestbranch, which reddens the suitefor every pr opened against it — ci only runs on
pull_request, so it wentunnoticed on the branch itself. included so this pr is green on its own merits;
it is an identical hunk and resolves cleanly whichever lands first. happy to drop
it if you would rather it stayed in one place.