Source code for app.schemas.progress_event
"""ProgressEvent v1 — discriminated union for the SSE stream.
Workers emit events to `events.jsonl` (one JSON line per event); the API
serves them via SSE with `Last-Event-ID` resume.
Schema is versioned: a `schema_version` literal is pinned per kind. New
kinds extend the union; old kinds never change shape.
.. rubric:: ProgressEvent kinds
:data:`ProgressEvent` is a tagged union discriminated on ``kind``:
- ``phase_started`` / ``phase_progress`` / ``phase_completed`` —
per-phase lifecycle (``phase`` is one of :data:`Phase`).
- ``metric`` — scalar telemetry sample (``key`` + ``value``).
- ``snapshot_available`` — a sealed snapshot is now readable
(``snapshot_seq`` + summary).
- ``log_line`` / ``warning`` / ``error`` — opaque message channels.
Forward-compatibility: SDKs MUST treat unknown ``kind`` values as
``unknown`` and surface them through a generic catch-all rather than
crashing the stream. New kinds are additive (new ``kind`` literal
plus new arm of the union); existing kinds never change shape.
``schema_version`` is pinned to ``1`` across every variant — bump it
in lock-step if the wire shape ever has to break, never per-kind.
"""
from __future__ import annotations
from datetime import datetime
from typing import Annotated, Any, Literal
from pydantic import BaseModel, Field
Phase = Literal[
"feature_extraction",
"matching",
"geometric_verification",
"incremental_init",
"incremental_register",
"incremental_ba",
"global_rotation_avg",
"global_positioning",
"global_ba",
"hierarchical_cluster",
"hierarchical_merge",
"panorama",
"spherical",
"bundle_adjust",
"triangulate",
"relocalize",
"pose_graph_optimize",
"segment",
"export",
"vlad_index",
"backend_action",
"artifact_conversion",
]
class _Base(BaseModel):
schema_version: Literal[1] = 1
ts: datetime
job_id: str
task_id: str | None = None
seq: int
[docs]
class PhaseStarted(_Base):
kind: Literal["phase_started"] = "phase_started"
phase: Phase
[docs]
class PhaseProgress(_Base):
kind: Literal["phase_progress"] = "phase_progress"
phase: Phase
current: int
total: int | None = None
rate: float | None = None # items/sec
[docs]
class PhaseCompleted(_Base):
kind: Literal["phase_completed"] = "phase_completed"
phase: Phase
[docs]
class Metric(_Base):
kind: Literal["metric"] = "metric"
key: str
value: float
[docs]
class SnapshotAvailable(_Base):
kind: Literal["snapshot_available"] = "snapshot_available"
snapshot_seq: int
summary: dict[str, Any]
[docs]
class LogLine(_Base):
kind: Literal["log_line"] = "log_line"
level: Literal["DEBUG", "INFO", "WARNING", "ERROR"]
message: str
[docs]
class Warning_(_Base):
kind: Literal["warning"] = "warning"
message: str
[docs]
class ErrorEvent(_Base):
kind: Literal["error"] = "error"
error_class: str
message: str
detail: dict[str, Any] | None = None
ProgressEvent = Annotated[
PhaseStarted
| PhaseProgress
| PhaseCompleted
| Metric
| SnapshotAvailable
| LogLine
| Warning_
| ErrorEvent,
Field(discriminator="kind"),
]
__all__ = [
"ErrorEvent",
"LogLine",
"Metric",
"Phase",
"PhaseCompleted",
"PhaseProgress",
"PhaseStarted",
"ProgressEvent",
"SnapshotAvailable",
"Warning_",
]