"""DTOs for ``/processing-log`` (admin-only GET). One row per (book, stage) attempt; the table drives the Operations "Processing log" page. Status is normalised to a typed enum on the wire even though the DB stores it as 1/0/NULL — the FE codegen produces a proper union over the three outcomes. """ from __future__ import annotations from enum import Enum from .common import ApiModel class ProcessingOutcomeEnum(str, Enum): """How the most recent (book, stage) attempt resolved. ``in_flight`` rows are runs that started but haven't finished — a crashed/killed subprocess shows up here until the reconciler marks it failed (BACKEND_BUILD.md §5.5). """ success = "success" failed = "failed" in_flight = "in_flight" class ProcessingLogEntryDTO(ApiModel): """One row of ``processing_log``. ``where`` is server-formatted (e.g. ``"bge-m3 (local / FlagEmbedding-cuda)"``) so the FE doesn't have to know how engine + provider + backend compose. Mirrors :func:`src.lib.processing.models.StageRun.where`. """ id: int book_id: str stage: str started_at: str finished_at: str | None = None outcome: ProcessingOutcomeEnum pages_done: int | None = None engine: str | None = None provider: str | None = None backend: str | None = None where: str error: str | None = None duration_ms: int | None = None