fix(document-processors): correct cascade escalation metric + review nits
Address round-1 review on PR #914: - Attribute the OCR hop in a fast->structured->ocr inline cascade to from_tier="structured" (not a second "fast" escalation), so astrolabe_document_escalation_total per-tier counts stay accurate. - Add test_inline_fast_structured_ocr_cascade pinning that two-hop path and the metric attribution. - Note in classify_from_text that its doc-level control ratio is over full_text (all pages), not the sampled subset classify_pdf uses. - Clarify that corrupt_glyphs never lands in the suppressed-escalation counter. - Dedupe the glyph-corrupt test string into tests/fixtures/glyph_corruption.py. - Use pytest.approx for the control-char-ratio zero checks (SonarCloud S1244). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
cf7209cd85
commit
d5286e39d6
@@ -407,9 +407,12 @@ def classify_from_text(
|
||||
# page_count guard also skips escalation; defaulting to 0.0 keeps the
|
||||
# recorded classification metric accurate rather than a misleading "ocr").
|
||||
ocr_frac = (sum(p.needs_ocr for p in pages) / sampled) if sampled else 0.0
|
||||
# Doc-level (char-weighted) control-char ratio -- the glyph-leak signal that
|
||||
# routes to the structured tier. Computed over full_text so it is robust to
|
||||
# boundary edge cases.
|
||||
# Doc-level control-char ratio -- the glyph-leak signal that routes to the
|
||||
# structured tier. Computed over the WHOLE full_text (all pages), unlike
|
||||
# classify_pdf which char-weights the up-to-MAX_SAMPLED_PAGES sample; the two
|
||||
# are therefore not numerically identical for a >24-page doc with corruption
|
||||
# concentrated outside the sample. full_text is used here because it is exactly
|
||||
# the text that gets chunked + indexed and is robust to boundary edge cases.
|
||||
control_ratio = _control_char_ratio(full_text)
|
||||
|
||||
flags, recommended = _route_from_signals(
|
||||
|
||||
@@ -247,6 +247,12 @@ class ProcessorRegistry:
|
||||
result, content, settings, record=True, filename=filename
|
||||
)
|
||||
|
||||
# The tier whose output produced the current ``classification`` -- used as
|
||||
# ``from_tier`` for a subsequent OCR hop so a fast->structured->ocr cascade
|
||||
# is attributed correctly (the OCR hop is from ``structured``, not a second
|
||||
# ``fast`` escalation).
|
||||
from_tier = "fast"
|
||||
|
||||
# Escalate a poor fast extraction up the ladder (fast -> structured -> ocr),
|
||||
# mirroring the external per-tier path so both modes behave identically. A
|
||||
# glyph-corrupt layer (the extractor leaked raw glyph codes -- the
|
||||
@@ -293,6 +299,7 @@ class ProcessorRegistry:
|
||||
)
|
||||
if structured_result.success:
|
||||
result = structured_result
|
||||
from_tier = "structured"
|
||||
classification = self._classify_result(
|
||||
result, content, settings, record=False, filename=filename
|
||||
)
|
||||
@@ -331,10 +338,11 @@ class ProcessorRegistry:
|
||||
if classification.total_chars == 0
|
||||
else "low_confidence"
|
||||
)
|
||||
record_document_escalation("fast", "ocr", reason)
|
||||
record_document_escalation(from_tier, "ocr", reason)
|
||||
logger.info(
|
||||
"Escalating %s fast->ocr (reason=%s)",
|
||||
"Escalating %s %s->ocr (reason=%s)",
|
||||
filename or "<bytes>",
|
||||
from_tier,
|
||||
reason,
|
||||
)
|
||||
ocr_result = await self._run_processor(
|
||||
|
||||
Reference in New Issue
Block a user