FastAPI service wrapping the Surya-OCR-2 model (datalab-to) served through vLLM: legacy /v1/api/ai/* endpoints, an OpenAI-compatible /v1/chat/completions endpoint, a coalescing request batcher, a local OCR CLI, Docker packaging, multilingual example outputs, and quantization/concurrency benchmarks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
47 lines
1.3 KiB
Python
47 lines
1.3 KiB
Python
import base64
|
|
import binascii
|
|
from typing import Any, Literal, Optional
|
|
|
|
from pydantic import (
|
|
BaseModel,
|
|
Field,
|
|
PrivateAttr,
|
|
StrictBool,
|
|
field_validator,
|
|
model_validator,
|
|
)
|
|
|
|
|
|
class Info(BaseModel):
|
|
_decoded_file: bytes = PrivateAttr(default=b"")
|
|
|
|
file: str # base64 string
|
|
type: Literal["png", "jpg", "jpeg", "gif"] # image type like 'png', 'jpg'
|
|
skip_text_detection: Optional[StrictBool] = Field(False)
|
|
skip_table_detection: Optional[StrictBool] = Field(False)
|
|
recognize_math: Optional[StrictBool] = Field(False)
|
|
ocr_with_boxes: Optional[StrictBool] = Field(True)
|
|
|
|
@field_validator("type", mode="before")
|
|
@classmethod
|
|
def normalize_type(cls, value: str) -> str:
|
|
if not isinstance(value, str):
|
|
raise ValueError("type must be a string")
|
|
return value.lower()
|
|
|
|
@model_validator(mode="after")
|
|
def decode_base64_file(self):
|
|
if not self.file:
|
|
raise ValueError("file must be a non-empty base64 string")
|
|
try:
|
|
self._decoded_file = base64.b64decode(self.file, validate=True)
|
|
except binascii.Error as exc:
|
|
raise ValueError("file must be valid base64") from exc
|
|
return self
|
|
|
|
|
|
class ApiResponse(BaseModel):
|
|
data: Any
|
|
message: str
|
|
code: int
|