All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 31s
141 lines
5 KiB
Python
141 lines
5 KiB
Python
import logging
|
|
import time
|
|
from dataclasses import dataclass, field
|
|
from typing import Any, Dict, List, Optional
|
|
from .base import BaseVendor, DocumentType, VendorResult, VerificationStatus
|
|
from .registry import registry, VendorRegistry
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@dataclass
|
|
class EngineResult:
|
|
"""Consolidated result returned by the VerificationEngine."""
|
|
is_success: bool
|
|
document_type: str
|
|
status: VerificationStatus
|
|
successful_vendor: Optional[str] = None
|
|
data: Dict[str, Any] = field(default_factory=dict)
|
|
attempts: List[Dict[str, Any]] = field(default_factory=list)
|
|
error_message: Optional[str] = None
|
|
total_latency_ms: float = 0.0
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
return {
|
|
"is_success": self.is_success,
|
|
"document_type": self.document_type,
|
|
"status": self.status.value if isinstance(self.status, VerificationStatus) else str(self.status),
|
|
"successful_vendor": self.successful_vendor,
|
|
"data": self.data,
|
|
"attempts_count": len(self.attempts),
|
|
"attempts": self.attempts,
|
|
"error_message": self.error_message,
|
|
"total_latency_ms": round(self.total_latency_ms, 2),
|
|
}
|
|
|
|
|
|
class VerificationEngine:
|
|
"""
|
|
Core waterfall orchestrator.
|
|
Executes multiple vendors in a fallback chain.
|
|
If vendor A errors or fails, the engine falls back to vendor B, C, etc.
|
|
"""
|
|
|
|
def __init__(self, vendor_registry: Optional[VendorRegistry] = None):
|
|
self.registry = vendor_registry or registry
|
|
|
|
def verify(
|
|
self,
|
|
document_type: str,
|
|
data: Dict[str, Any],
|
|
files: Optional[Dict[str, Any]] = None,
|
|
preferred_vendor: Optional[str] = None,
|
|
) -> EngineResult:
|
|
"""
|
|
Executes document verification through the vendor waterfall.
|
|
"""
|
|
start_time = time.time()
|
|
doc_type_clean = document_type.lower()
|
|
vendors = self.registry.get_vendors_for_document(doc_type_clean, preferred_vendor=preferred_vendor)
|
|
|
|
if not vendors:
|
|
total_latency = (time.time() - start_time) * 1000
|
|
return EngineResult(
|
|
is_success=False,
|
|
document_type=doc_type_clean,
|
|
status=VerificationStatus.ERROR,
|
|
error_message=f"No active vendors configured for document type '{document_type}'",
|
|
total_latency_ms=total_latency,
|
|
)
|
|
|
|
attempts: List[Dict[str, Any]] = []
|
|
|
|
logger.info(
|
|
"Starting verification for '%s'. Candidate vendors in order: %s",
|
|
doc_type_clean,
|
|
[v.name for v in vendors],
|
|
)
|
|
|
|
for attempt_index, vendor in enumerate(vendors, start=1):
|
|
logger.info(
|
|
"Attempt #%d: Dispatching verification to vendor '%s'",
|
|
attempt_index,
|
|
vendor.name,
|
|
)
|
|
|
|
result: VendorResult = vendor.verify(doc_type_clean, data, files)
|
|
attempt_record = result.to_dict()
|
|
attempt_record["attempt_number"] = attempt_index
|
|
attempts.append(attempt_record)
|
|
|
|
if result.is_success:
|
|
total_latency = (time.time() - start_time) * 1000
|
|
logger.info(
|
|
"Verification SUCCEEDED via vendor '%s' on attempt #%d (latency=%.2f ms)",
|
|
vendor.name,
|
|
attempt_index,
|
|
result.latency_ms,
|
|
)
|
|
return EngineResult(
|
|
is_success=True,
|
|
document_type=doc_type_clean,
|
|
status=VerificationStatus.SUCCESS,
|
|
successful_vendor=vendor.name,
|
|
data=result.data,
|
|
attempts=attempts,
|
|
total_latency_ms=total_latency,
|
|
)
|
|
else:
|
|
logger.warning(
|
|
"Vendor '%s' failed on attempt #%d: status=%s, code=%s, error=%s. Triggering fallback to next vendor.",
|
|
vendor.name,
|
|
attempt_index,
|
|
result.status,
|
|
result.error_code,
|
|
result.error_message,
|
|
)
|
|
|
|
# If all vendors failed
|
|
total_latency = (time.time() - start_time) * 1000
|
|
last_error = attempts[-1].get("error_message") if attempts else "All vendors failed"
|
|
logger.error(
|
|
"All %d vendors failed verification for '%s'. Total time=%.2f ms",
|
|
len(vendors),
|
|
doc_type_clean,
|
|
total_latency,
|
|
)
|
|
|
|
return EngineResult(
|
|
is_success=False,
|
|
document_type=doc_type_clean,
|
|
status=VerificationStatus.FAILED,
|
|
successful_vendor=None,
|
|
data={},
|
|
attempts=attempts,
|
|
error_message=f"All vendors exhausted. Last error: {last_error}",
|
|
total_latency_ms=total_latency,
|
|
)
|
|
|
|
|
|
# Global default engine instance
|
|
engine = VerificationEngine()
|