Решение владельца 08.10.2026: recognition.tabular.request → files.tabular_uploaded, recognition.image.request → files.img_uploaded, recognition.result → files.tabular_parsed (общий subject ack и результатов; files.img_parsed не используется). Тела сообщений, Nats-Msg-Id, queue group'ы и schema_version не меняются. Константы api/src/contract/subjects.py, докстринги, тесты, README, CLAUDE.md; копии схем api/contracts/ из docs/contracts/ побайтно; change nats-files-subjects с дельтой спеки. Сквозной change — RSHB/arm-ka-docs#74. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
64 lines
2.4 KiB
Python
64 lines
2.4 KiB
Python
"""Конверт сообщения на `files.tabular_parsed`: `recognition-result-message.schema.json` v1.0.
|
||
|
||
`processing` — ack «взял в работу» (до чтения файла из S3); `done | needs_check | error` — итог.
|
||
Условная обязательность — буквально из `allOf` схемы: для `done | needs_check` обязательны `result_s3` и
|
||
`result_schema_version`; для `error` — `error`. Других правил нет (например, `error` при `done` схема не запрещает).
|
||
"""
|
||
|
||
from enum import StrEnum
|
||
from typing import Annotated, Self
|
||
|
||
from pydantic import Field, model_validator
|
||
|
||
from src.contract.common import (
|
||
ContractModel,
|
||
ContractUUID,
|
||
LaxErrorCode,
|
||
Metrics,
|
||
OptionalNotNull,
|
||
ResultSchemaVersion,
|
||
S3Ref,
|
||
SchemaVersion,
|
||
ServiceInfo,
|
||
)
|
||
|
||
|
||
class MessageStatus(StrEnum):
|
||
PROCESSING = "processing"
|
||
DONE = "done"
|
||
NEEDS_CHECK = "needs_check"
|
||
ERROR = "error"
|
||
|
||
|
||
LaxMessageStatus = Annotated[MessageStatus, Field(strict=False)]
|
||
|
||
|
||
class MessageError(ContractModel):
|
||
"""`error {code, message}` конверта: оба поля обязательны."""
|
||
|
||
code: LaxErrorCode
|
||
message: str
|
||
|
||
|
||
class RecognitionResultMessage(ContractModel):
|
||
schema_version: SchemaVersion
|
||
job_id: ContractUUID # recognition_job.id
|
||
file_id: ContractUUID # stored_file.id
|
||
status: LaxMessageStatus
|
||
service: ServiceInfo
|
||
result_s3: OptionalNotNull[S3Ref] = None # results/{file_id}/{job_id}/result.json
|
||
result_schema_version: OptionalNotNull[ResultSchemaVersion] = None # schema_version файла результата
|
||
error: OptionalNotNull[MessageError] = None
|
||
raw_s3_key: str | None = None # scan-gateway: raw/{job_id}.json; в БД не регистрируется
|
||
metrics: OptionalNotNull[Metrics] = None
|
||
|
||
@model_validator(mode="after")
|
||
def _check_status_requirements(self) -> Self:
|
||
if self.status in (MessageStatus.DONE, MessageStatus.NEEDS_CHECK):
|
||
missing = [name for name in ("result_s3", "result_schema_version") if getattr(self, name) is None]
|
||
if missing:
|
||
raise ValueError(f"при status = {self.status.value} обязательны поля: {', '.join(missing)}")
|
||
elif self.status is MessageStatus.ERROR and self.error is None:
|
||
raise ValueError("при status = error обязательно поле error")
|
||
return self
|