Commit Graph
55 Commits
Author SHA1 Message Date
Alex-Gilbert 74b9820a48 font debug analysis 2026-02-23 21:17:22 -06:00
Alex-GilbertandClaude Opus 4.6 672bdc3223 fix: batch LLM validation, stream results, improve scoring prompt
- Batch candidates in groups of 50 (was 200) to avoid output token
  truncation that caused all-zero scores
- Stream scored results via SSE as each batch completes instead of
  waiting for all batches to finish
- Include solve_id in done event so validate works when results fit
  in a single page
- Rewrite prompt to judge sentence coherence ("does it make sense")
  rather than word predictability ("is it expected")
- Frontend inserts results in score-sorted order as they arrive

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-23 21:17:00 -06:00
Alex-GilbertandClaude Opus 4.6 f7dd5a342a feat: add POST /api/solve/{id}/validate endpoint
Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-23 16:19:21 -06:00
Alex-GilbertandClaude Opus 4.6 29a41e650c feat: add LLM validation module with prompt and tool schema
Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-23 16:18:40 -06:00
Alex-GilbertandClaude Opus 4.6 a0e2bf6ec1 feat: disable two-word adjective+noun search by default
The new Wiktionary dictionary already includes 31k multi-word nouns,
making the combinatorial adjective+noun phase redundant. Add two_word
parameter (default False) to solve_word_dictionary to opt in when needed.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-23 15:43:00 -06:00
Alex-GilbertandClaude Opus 4.6 b51dc18d9c feat: clean up solve buffer on cancel
Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-23 14:50:40 -06:00
Alex-GilbertandClaude Opus 4.6 6341bcdfa3 feat: add GET /api/solve/{id}/results pagination endpoint
Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-23 14:50:07 -06:00
Alex-GilbertandClaude Opus 4.6 f0b444d26c feat: cap SSE stream at PAGE_SIZE and buffer remaining results
Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-23 14:49:30 -06:00
Alex-GilbertandClaude Opus 4.6 d04116579b feat: add two-word binary search solver (phase 2 of word mode)
Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-23 11:06:10 -06:00
Alex-GilbertandClaude Opus 4.6 6c6a90e796 feat: add single-word noun solver (phase 1 of word mode)
Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-23 10:05:52 -06:00
Alex-GilbertandClaude Opus 4.6 f13590f80d feat: add noun/adjective/plural word list loaders
Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-23 10:02:11 -06:00
Alex-GilbertandClaude Opus 4.6 6e77070538 fix: known_start/end casing for multi-word name width calculation
When known_start was set with capitalized casing, the unknown portion was
forced to all-lowercase, breaking width measurements for multi-word names
(e.g., "hn doe" instead of "hn Doe"). Added _case_unknown_portion helper
that respects word boundaries. Also fixed effective_left/right kerning
context to use correctly cased characters from the display string.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-22 14:44:33 -06:00
Alex-GilbertandClaude Opus 4.6 f1e25f9712 feat: add LLM boundary detection for spot redaction path
analyze_spot_redaction is now async and calls identify_boundary_text()
to use Claude Haiku for clean left/right text extraction instead of
center-point character filtering. Falls back to center-point if LLM
is unavailable.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-20 14:46:45 -06:00
Alex-GilbertandClaude Opus 4.6 2783ed2615 feat: spot endpoint returns full analysis data
Enrich POST /api/doc/{doc_id}/page/{page}/spot to call
analyze_spot_redaction when OCR data is cached, returning full
analysis JSON (segments, gap, font, line, offsets) alongside the
bounding box. This makes spot redactions first-class, matching the
analysis format returned by get_page_data for auto-detected redactions.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-20 12:42:18 -06:00
Alex-GilbertandClaude Opus 4.6 8857793759 feat: add analyze_spot_redaction for single-box analysis
Add a synchronous function that runs the analysis mini-pipeline on a
single known bounding box using cached OCR data. This enables spot
redaction endpoints to get full analysis (font, left/right context text,
pixel alignment offsets) without needing LLM detection.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-20 12:39:57 -06:00
Alex-GilbertandClaude Opus 4.6 f24a710b9e feat: allow analyze_page to accept pre-computed OCR lines
Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-20 12:36:58 -06:00
Alex-GilbertandClaude Opus 4.6 ced68f2321 feat: add /api/doc/{id}/ocr SSE endpoint for cached OCR
Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-20 12:36:46 -06:00
Alex-GilbertandClaude Opus 4.6 4a92cb8463 fix: update app tests for mode/charset restructuring
Replaced test_solve_endpoint_dictionary and test_dictionary_crud with
test_solve_endpoint_name_mode testing the new name mode.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-18 13:16:57 -06:00
Alex-GilbertandClaude Opus 4.6 d0bd7967e4 feat: update solve_full_name_dictionary with casing and known_start/known_end
Replaces uppercase_only bool with casing string parameter. Adds
known_start/known_end support for filtering and gap-width measurement.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-18 13:11:53 -06:00
Alex-GilbertandClaude Opus 4.6 d32f27cb8d feat: add solve_name_dictionary for single-word associate name matching
Supports casing (lowercase/uppercase/capitalized), known_start/known_end
filtering with gap-width measurement of the unknown portion only.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-18 13:11:13 -06:00
Alex-GilbertandClaude Opus 4.6 7b0802d005 feat: add solve_full_name_dictionary with Cartesian product matching
Generate first x last name combinations from associate lists, apply
Title Case or UPPER casing, measure rendered widths, and return matches
within tolerance. Also checks associate name variants (initials,
nicknames) with deduplication.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-18 12:00:45 -06:00
Alex-GilbertandClaude Opus 4.6 b50a32a038 fix(solver): add max_results cap to prevent OOM in full-name solver
The full-name solver collected all results into a Vec with no limit,
causing OOM crashes for realistic targets where millions of valid
first×last combinations exist. Thread max_results through
solve_subtree, dfs_first, and the Cartesian product loop so the
search stops early once enough results are found.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-18 11:20:16 -06:00
Alex-Gilbert a5505c1fbb attempting to make redaction guessing better 2026-02-18 10:35:42 -06:00
Alex-GilbertandClaude Opus 4.6 0f9c7ff858 feat: add page analysis pipeline orchestrating OCR, LLM, OpenCV, and font detection
Wire together the per-page pipeline: OCR -> LLM redaction detection ->
guided OpenCV search -> font detection with masking -> pixel alignment.
Includes font caching per line and progress callbacks.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-17 15:31:17 -06:00
Alex-GilbertandClaude Opus 4.6 8b5511e7fd feat: add detect_font_masked for redaction-aware font detection
Adds a new detect_font_masked() function that masks redaction boxes to
white (255) before running pixel-based font detection on the full line
crop. This replaces the fragile approach of cropping just the left/right
portion of a line, which lost signal from narrow crops and coordinate
mismatches. The Dice-based scoring naturally ignores masked (white)
regions since they contain no ink pixels.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-17 15:26:14 -06:00
Alex-GilbertandClaude Opus 4.6 c39d74e46d feat: add guided OpenCV search for LLM-directed redaction regions
Add find_redaction_in_region() that searches for a black rectangle
within a specific page region identified by the LLM. Uses a lower
minimum area threshold (100px vs 500px) since the LLM provides
spatial guidance, preventing merged redactions and false positives.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-17 15:22:28 -06:00
Alex-GilbertandClaude Opus 4.6 51ef030322 feat: add LLM-based redaction detection module
Introduce llm_detect.py that sends OCR lines to Claude Haiku to identify
redaction locations via broken text patterns (garbled chars, brackets,
pipes). The LLM returns boundary words which are mapped back to pixel
positions using OCR character data. Includes comprehensive tests covering
single/multiple redactions, line start/end edge cases, and error handling.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-17 13:59:49 -06:00
Alex-GilbertandClaude Opus 4.6 a2ef97b349 feat: pixel-aligned text positioning for redaction overlay
Replace OCR-based offset calculation with pixel alignment that slides
rendered text across the page image to find the exact position with
maximum Dice overlap. Also fix char filtering to use center-point
(c.x + c.w/2) instead of right-edge, recovering characters like "nt"
in "Sent" that partially overlap the redaction box.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-17 13:02:36 -06:00
Alex-GilbertandClaude Opus 4.6 93f4502bfa fix: update score docstring, remove dead SIZE_RANGE, fix unused import
Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-17 11:39:13 -06:00
Alex-GilbertandClaude Opus 4.6 1b863fdf21 feat: wire pixel scoring into _full_search and _fine_search
Replace word-width MAE scoring with pixel-overlap Dice scoring in both
_full_search() and _fine_search(). Both functions now accept a line_crop
parameter and use _score_font_line_pixel() instead of _score_font_line().
Comparisons are flipped (higher is better). The size range is now derived
from the OCR line height instead of the fixed SIZE_RANGE constant, and
uses step=1 since pixel scoring has sharp peaks at the exact font size.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-17 11:33:10 -06:00
Alex-GilbertandClaude Opus 4.6 043aae5abb feat: add pixel-based font scoring function
Add _score_font_line_pixel() which renders candidate text and compares
pixels against the actual page image using Dice coefficient overlap.
This captures glyph shape differences that the old word-width MAE
approach cannot distinguish (e.g. serif vs sans-serif).

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-17 11:16:20 -06:00
Alex-GilbertandClaude Opus 4.6 3c867de6aa feat: add offset_x/offset_y to analyze response for alignment guess
Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-16 15:20:11 -06:00
Alex-GilbertandClaude Opus 4.6 4a8716539d feat: add /api/redaction/analyze endpoint for on-demand OCR + font detection
When a user clicks a redaction, this endpoint crops the surrounding line,
runs Tesseract OCR, detects the font, and returns text segments (before/after
the redaction), gap width, font info, and character-level bounding boxes.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-16 14:40:17 -06:00
Alex-GilbertandClaude Opus 4.6 c0f52772ee refactor: simplify upload endpoint to use redaction detection instead of OCR
Replace upfront OCR + font detection + overlay rendering with lightweight
OpenCV-based redaction detection. Upload now just rasterizes and detects
black rectangles, making it significantly faster. Remove the overlay
endpoint and update the data endpoint to return redaction bboxes.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-16 14:37:54 -06:00
Alex-GilbertandClaude Opus 4.6 793ab1c970 feat: add OpenCV redaction detection module
Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-16 14:34:22 -06:00
Alex-GilbertandClaude Opus 4.6 0f4ef6b816 feat: add emails solve mode with kerning-aware width matching
Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-16 13:39:19 -06:00
Alex-GilbertandClaude Opus 4.6 2268e8549e feat: add separate V (victim) badge independent of associate tiers
Victim names are stored as a flat set of lowercased name variants
with no person details — the V badge appears but is not clickable,
protecting victim identities. A result can show both T1/T2/T3 and V
badges simultaneously.

- 15 known victims from public court records → 43 name variants
- Enabler/perpetrator categories promoted to T2
- Purple V badge with pointer-events disabled
- Victim matching uses prefix/suffix combination like associate matching

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-16 10:31:38 -06:00
Alex-GilbertandClaude Opus 4.6 4bfe7c85a7 feat: add GET /api/associates endpoint
Lazy-loads associates.json from data dir and serves it at /api/associates,
with fallback to empty dicts if file is missing.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-16 07:51:07 -06:00
Alex-GilbertandClaude Opus 4.6 8011cf4da2 feat: add associates data processor with nickname generation and tiering
Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-16 07:46:56 -06:00
Alex-GilbertandClaude Opus 4.6 fc9eff2da5 test: add round-trip integration test for solver
Measures a known word's width in font context, then verifies the solver
recovers that word from its width. Proves the width table and solver
work correctly end-to-end.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-15 22:26:20 -06:00
Alex-GilbertandClaude Opus 4.6 d391bfa6d6 feat: SSE solve endpoint with dictionary and enumerate modes
Add POST /api/solve endpoint that streams results via Server-Sent Events,
supporting both enumerate (brute-force) and dictionary solve modes. Add
dictionary CRUD endpoints (POST/GET/DELETE /api/dictionary) and solve
cancellation (DELETE /api/solve/{id}). Move static files mount to end of
app.py so API routes take priority.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-15 22:20:20 -06:00
Alex-GilbertandClaude Opus 4.6 b357713e5e feat: dictionary solver with in-memory word list store
Add solve_dictionary() for linear-scan matching of dictionary entries
against a target pixel width, and DictionaryStore for managing named
word/name lists. Reuses SolveResult from the solver module.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-15 22:15:21 -06:00
Alex-GilbertandClaude Opus 4.6 11cc29d3f8 feat: parallel solver with multiprocessing via ProcessPoolExecutor
Distribute branch-and-bound search across CPU cores by generating
depth-2 prefixes and farming each subtree to a worker process.
Includes on_progress callback and automatic prefix depth selection.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-15 22:12:49 -06:00
Alex-GilbertandClaude Opus 4.6 9021cf8a21 feat: single-threaded branch-and-bound gap solver
Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-15 22:07:41 -06:00
Alex-GilbertandClaude Opus 4.6 696b0165ef feat: width table module with kerning-aware font metrics
Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-15 22:00:12 -06:00
Alex-GilbertandClaude Opus 4.6 b9df273521 Add interactive font tuning UI with split-panel layout
Replace static overlay with client-side canvas rendering. Left panel
shows clickable OCR lines with font controls (dropdown + size slider).
Right panel renders green text overlay on the document via canvas.
Fonts loaded from backend TTF files using the FontFace API.

New endpoints: GET /api/fonts, GET /api/font/{id}

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-15 20:17:57 -06:00
Alex-GilbertandClaude Opus 4.6 d2b149f1ba feat: per-line font detection with prior bias
Instead of detecting one font per page, detect the font for each line
independently. The previous line's font is used as a prior — if it
scores within 15% of the best candidate, it's kept for consistency.
This handles documents with mixed fonts (bold headers, regular body,
different sections).

Also: redirect / to the static UI, fix data endpoint to return
per-line font info.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-15 19:24:48 -06:00
Alex-Gilbert a9c394151d test: end-to-end smoke test with visual output 2026-02-15 19:06:24 -06:00
Alex-GilbertandClaude Opus 4.6 8d82054b5d feat: overlay calibration with word-by-word rendering
- Render each word at its OCR'd position instead of whole lines,
  so word spacing matches the original document exactly
- Add y-offset correction using glyph bounding box
- Add fine-grained font size search (±3 around coarse best, step 1)
- Add alignment quality test (48.9% hit rate, >30% threshold)
- Add scipy to dev dependencies

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-15 19:04:40 -06:00
Alex-GilbertandClaude Opus 4.6 c95b6cb9d7 feat: FastAPI backend with upload, overlay, and data endpoints
Adds POST /api/upload (runs full pipeline: rasterize, OCR, font detect,
overlay), GET endpoints for original/overlay images and OCR data JSON.
Also adds python-multipart, anyio, and pytest-anyio dependencies.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-15 18:53:48 -06:00