from typing import List, Optional from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator class SentenceBoundary(BaseModel): model_config = ConfigDict(extra="forbid") index: int = Field(ge=0) start_ms: int = Field(ge=0) end_ms: int = Field(gt=0) text: Optional[str] = None @model_validator(mode="after") def validate_range(self): if self.end_ms <= self.start_ms: raise ValueError("end_ms must be greater than start_ms") return self class SentenceBoundaryDocument(BaseModel): model_config = ConfigDict(extra="forbid") video_hash: str duration_ms: int = Field(gt=0) algorithm_version: str = Field(min_length=1) sentences: List[SentenceBoundary] @field_validator("video_hash") @classmethod def validate_video_hash(cls, value: str) -> str: normalized = value.strip().lower() if len(normalized) != 64 or any(char not in "0123456789abcdef" for char in normalized): raise ValueError("video_hash must be a SHA-256 hex digest") return normalized @model_validator(mode="after") def validate_sentences(self): previous_end = 0 for expected_index, sentence in enumerate(self.sentences): if sentence.index != expected_index: raise ValueError("sentence indexes must be contiguous and zero-based") if sentence.start_ms < previous_end: raise ValueError("sentences must be sorted and non-overlapping") if sentence.end_ms > self.duration_ms: raise ValueError("sentence end_ms cannot exceed duration_ms") previous_end = sentence.end_ms return self