"""Выбор модели по MAJOR `schema_version` на окне перехода v1 → v2 (ядро принимает оба MAJOR). `schema_version` — `MAJOR.MINOR`. Известны MAJOR 1 и 2; иной MAJOR, неверный формат или отсутствие поля — `pydantic.ValidationError` (разбор отдан модели v1, чей шаблон `^1\\.\\d+$` такое не принимает). """ import json import re from typing import Any from src.contract.message import RecognitionResultMessage, RecognitionResultMessageV2 from src.contract.result import RecognitionResult from src.contract.result_v2 import RecognitionResultV2 _VERSION = re.compile(r"(\d+)\.\d+", re.ASCII) MAJOR_V2 = 2 __all__ = ["major", "parse_message", "parse_result"] def major(schema_version: str) -> int: """MAJOR из `MAJOR.MINOR`; иная запись — `ValueError`.""" match = _VERSION.fullmatch(schema_version) if match is None: raise ValueError(f"schema_version {schema_version!r}: ожидается MAJOR.MINOR") return int(match.group(1)) def _is_v2(data: Any) -> bool: version = data.get("schema_version") if isinstance(data, dict) else None if not isinstance(version, str): return False try: return major(version) == MAJOR_V2 except ValueError: return False def parse_message(data: dict[str, Any]) -> RecognitionResultMessage | RecognitionResultMessageV2: """Конверт `recognition-result-message`: MAJOR 1 → модель v1, MAJOR 2 → модель v2, иное — `ValidationError`.""" if _is_v2(data): return RecognitionResultMessageV2.model_validate(data) return RecognitionResultMessage.model_validate(data) def parse_result(content: bytes) -> RecognitionResult | RecognitionResultV2: """Файл результата: те же правила MAJOR; нечитаемый JSON — `ValueError` (`json.JSONDecodeError`).""" data = json.loads(content) if _is_v2(data): return RecognitionResultV2.model_validate(data) return RecognitionResult.model_validate(data)