""" Quality pipeline — defensive wrapper around the L0 checks. The pipeline is the integration point for the route. It: 1. Catches all exceptions (L0 must NEVER break a translation job) 2. Adds timing 3. Emits a single structured log line per job The actual checks live in `script_detector`, `length_checker`, `pattern_leak`. This module is the orchestration / safety layer. """ from __future__ import annotations import time from typing import List, Optional from core.logging import get_logger from .script_detector import evaluate_document, DocumentQualityResult logger = get_logger(__name__) def run_l0_check( source_chunks: List[str], translated_chunks: List[str], target_lang: Optional[str], job_id: Optional[str] = None, file_extension: Optional[str] = None, ) -> DocumentQualityResult: """ Run the L0 quality checks defensively. Never raises. Returns an empty/neutral DocumentQualityResult on internal error so the calling route can log and continue without affecting the translation job outcome. """ start = time.time() empty = DocumentQualityResult( passed=True, score=0.0, chunk_count=0, failed_chunk_count=0, issues={"internal_error": 1}, ) try: result = evaluate_document(source_chunks, translated_chunks, target_lang) except Exception as e: elapsed_ms = round((time.time() - start) * 1000, 2) logger.warning( "quality_l0_check_failed", job_id=job_id, file_extension=file_extension, error=str(e)[:200], error_type=type(e).__name__, elapsed_ms=elapsed_ms, ) return empty elapsed_ms = round((time.time() - start) * 1000, 2) logger.info( "quality_l0_check", job_id=job_id, file_extension=file_extension, target_lang=target_lang, chunk_count=result.chunk_count, failed_chunk_count=result.failed_chunk_count, score=result.score, passed=result.passed, issues=result.issues, elapsed_ms=elapsed_ms, ) return result