test(ingest): cover ocr-incluster routing + fix scan-gate & batch guard
Address claude-review on #922: Blocking — test coverage for the new tier2 rung: - test_registry_tiering.py: inline empty-text routes to ocr-incluster before ocr-upstream; only-incluster-enabled routes to incluster; disabled-incluster skips to upstream; evaluate_escalation empty_text hops to ocr-incluster (and falls through to upstream when incluster off); next_available_tier walks the full fast→structured→ocr-incluster→ocr-upstream ladder + ignore_ocr_enabled ideal-target. - test_escalation_signature.py: enabling document_ocr_incluster_enabled changes the dead-letter signature (independent of the upstream rung). - test_tiered_escalation_strategy.py: structured→ocr-incluster hops to INGEST_QUEUE_OCR_INCLUSTER; tier_for_queue covers the in-cluster queue. Important — real fixes: - registry.py: run scan detection (image_coverage_per_page) when EITHER OCR rung is enabled, not just the upstream one — a tenant with only in-cluster OCR on was missing image-coverage scan signals. - ocr.py: the gateway_only (in-cluster) processor never enters batch mode — the GPU is synchronous/low-latency; batch OCR is the upstream Mistral async path. _get_batch_client short-circuits to None. Covered by a new test. Nit: - cli.py: worker --tier help lists ocr-incluster/ocr-upstream as separate fleets. Left as-is: the lazy anyio.Lock init in OcrProcessor — instances ARE created at module import (document_processors/__init__.py), so deferring lock creation off import time is still required; moving it into __init__ would reintroduce the import-time-primitive issue the comment guards against. 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
c21804fbbc
commit
87b8edd139
@@ -45,6 +45,7 @@ class TestLadder:
|
||||
assert TIER_LADDER == ("fast", "structured", "ocr-incluster", "ocr-upstream")
|
||||
|
||||
def test_tier_for_queue(self):
|
||||
assert pq.tier_for_queue(pq.INGEST_QUEUE_OCR_INCLUSTER) == "ocr-incluster"
|
||||
assert pq.tier_for_queue(pq.INGEST_QUEUE_OCR_UPSTREAM) == "ocr-upstream"
|
||||
assert pq.tier_for_queue(pq.INGEST_QUEUE_STRUCTURED) == "structured"
|
||||
# Legacy / unknown / None all fall back to the cheapest tier.
|
||||
@@ -64,6 +65,16 @@ class TestTieredEscalationStrategy:
|
||||
assert decision is not None
|
||||
assert decision.queue == pq.INGEST_QUEUE_OCR_UPSTREAM
|
||||
|
||||
def test_escalate_hops_to_incluster_queue(self):
|
||||
# tier2 in-cluster (Deck #353): structured -> ocr-incluster lands on the
|
||||
# in-cluster queue the GPU sentinel watches, not the paid upstream queue.
|
||||
exc = EscalateError(
|
||||
from_tier="structured", to_tier="ocr-incluster", reason="empty_text"
|
||||
)
|
||||
decision = self._strategy().get_retry_decision(exception=exc, job=_job())
|
||||
assert decision is not None
|
||||
assert decision.queue == pq.INGEST_QUEUE_OCR_INCLUSTER
|
||||
|
||||
def test_escalate_to_structured(self):
|
||||
exc = EscalateError(
|
||||
from_tier="fast", to_tier="structured", reason="low_confidence"
|
||||
|
||||
Reference in New Issue
Block a user