feat: add core OCR API integration (refs #65)
All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 5m59s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 31s
Deploy to Staging / Verify Staging (pull_request) Successful in 2m19s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 7s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped
All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 5m59s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 31s
Deploy to Staging / Verify Staging (pull_request) Successful in 2m19s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 7s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped
OCR Service (Python/FastAPI):
- POST /extract for synchronous OCR extraction
- POST /jobs and GET /jobs/{job_id} for async processing
- Image preprocessing (deskew, denoise) for accuracy
- HEIC conversion via pillow-heif
- Redis job queue for async processing
Backend (Fastify):
- POST /api/ocr/extract - authenticated proxy to OCR
- POST /api/ocr/jobs - async job submission
- GET /api/ocr/jobs/:jobId - job polling
- Multipart file upload handling
- JWT authentication required
File size limits: 10MB sync, 200MB async
Processing time target: <3 seconds for typical photos
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
18
ocr/app/models/__init__.py
Normal file
18
ocr/app/models/__init__.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""Pydantic models for OCR service."""
|
||||
from .schemas import (
|
||||
DocumentType,
|
||||
ExtractedField,
|
||||
JobResponse,
|
||||
JobStatus,
|
||||
JobSubmitRequest,
|
||||
OcrResponse,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"DocumentType",
|
||||
"ExtractedField",
|
||||
"JobResponse",
|
||||
"JobStatus",
|
||||
"JobSubmitRequest",
|
||||
"OcrResponse",
|
||||
]
|
||||
65
ocr/app/models/schemas.py
Normal file
65
ocr/app/models/schemas.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""Pydantic models for OCR API request/response validation."""
|
||||
from enum import Enum
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class DocumentType(str, Enum):
|
||||
"""Types of documents that can be processed."""
|
||||
|
||||
VIN = "vin"
|
||||
RECEIPT = "receipt"
|
||||
MANUAL = "manual"
|
||||
UNKNOWN = "unknown"
|
||||
|
||||
|
||||
class ExtractedField(BaseModel):
|
||||
"""A single extracted field with confidence score."""
|
||||
|
||||
value: str
|
||||
confidence: float = Field(ge=0.0, le=1.0)
|
||||
|
||||
|
||||
class OcrResponse(BaseModel):
|
||||
"""Response from OCR extraction."""
|
||||
|
||||
success: bool
|
||||
document_type: DocumentType = Field(alias="documentType")
|
||||
raw_text: str = Field(alias="rawText")
|
||||
confidence: float = Field(ge=0.0, le=1.0)
|
||||
extracted_fields: dict[str, ExtractedField] = Field(
|
||||
default_factory=dict, alias="extractedFields"
|
||||
)
|
||||
processing_time_ms: int = Field(alias="processingTimeMs")
|
||||
|
||||
model_config = {"populate_by_name": True}
|
||||
|
||||
|
||||
class JobStatus(str, Enum):
|
||||
"""Status of an async OCR job."""
|
||||
|
||||
PENDING = "pending"
|
||||
PROCESSING = "processing"
|
||||
COMPLETED = "completed"
|
||||
FAILED = "failed"
|
||||
|
||||
|
||||
class JobResponse(BaseModel):
|
||||
"""Response for async job status."""
|
||||
|
||||
job_id: str = Field(alias="jobId")
|
||||
status: JobStatus
|
||||
progress: Optional[int] = Field(default=None, ge=0, le=100)
|
||||
result: Optional[OcrResponse] = None
|
||||
error: Optional[str] = None
|
||||
|
||||
model_config = {"populate_by_name": True}
|
||||
|
||||
|
||||
class JobSubmitRequest(BaseModel):
|
||||
"""Request to submit an async OCR job."""
|
||||
|
||||
callback_url: Optional[str] = Field(default=None, alias="callbackUrl")
|
||||
|
||||
model_config = {"populate_by_name": True}
|
||||
Reference in New Issue
Block a user