feat(observatory): verify exact published result reuse
This commit is contained in:
@@ -54,6 +54,7 @@ from k1link.observatory.portable_queue_binding import (
|
||||
PortableQueueBindingError,
|
||||
PortableRecordedQueueBindingService,
|
||||
)
|
||||
from k1link.observatory.portable_result_cache import PortableResultCache
|
||||
from k1link.observatory.portable_result_contract import (
|
||||
PortableCalculationProfileRegistry,
|
||||
PortableResultContractValidatorRegistry,
|
||||
@@ -538,24 +539,41 @@ else:
|
||||
OBSERVATORY_PORTABLE_BINDING_SERVICE: PortableRecordedQueueBindingService | None
|
||||
OBSERVATORY_PORTABLE_SETUP_PROJECTOR: PortableSetupProjector | None
|
||||
OBSERVATORY_PORTABLE_SETUP_PROJECTOR_ERROR: str | None
|
||||
OBSERVATORY_PORTABLE_RESULT_CACHE: PortableResultCache | None = None
|
||||
try:
|
||||
if OBSERVATORY_PORTABLE_DEFINITION_REGISTRY is None:
|
||||
raise PortableSetupProjectionError(
|
||||
OBSERVATORY_PORTABLE_DEFINITION_REGISTRY_ERROR
|
||||
or "portable definition registry is unavailable"
|
||||
)
|
||||
if (
|
||||
session_artifact_gateway is not None
|
||||
and OBSERVATORY_RECORDED_JOB_QUEUE is not None
|
||||
and OBSERVATORY_PORTABLE_CALCULATION_PROFILES is not None
|
||||
):
|
||||
OBSERVATORY_PORTABLE_RESULT_CACHE = PortableResultCache(
|
||||
sessions=session_store, artifacts=session_artifact_gateway.store,
|
||||
queue=OBSERVATORY_RECORDED_JOB_QUEUE,
|
||||
definitions=OBSERVATORY_PORTABLE_DEFINITION_REGISTRY,
|
||||
calculation_profiles=OBSERVATORY_PORTABLE_CALCULATION_PROFILES,
|
||||
)
|
||||
OBSERVATORY_PORTABLE_BINDING_SERVICE = PortableRecordedQueueBindingService(
|
||||
data_dir=session_store.data_dir,
|
||||
session_store=session_store,
|
||||
media_inspector=session_recorded_media_inspector,
|
||||
definitions=OBSERVATORY_PORTABLE_DEFINITION_REGISTRY,
|
||||
queue=OBSERVATORY_RECORDED_JOB_QUEUE,
|
||||
published_result_available=(
|
||||
None if OBSERVATORY_PORTABLE_RESULT_CACHE is None
|
||||
else OBSERVATORY_PORTABLE_RESULT_CACHE.available
|
||||
),
|
||||
)
|
||||
OBSERVATORY_PORTABLE_SETUP_PROJECTOR = PortableSetupProjector(
|
||||
registry=OBSERVATORY_PORTABLE_DEFINITION_REGISTRY,
|
||||
capability_probe=OBSERVATORY_PORTABLE_BINDING_SERVICE,
|
||||
dispatch_available=OBSERVATORY_WORKER_DISPATCH_READY,
|
||||
equipment_capture_registry=session_store.equipment_capture_registry,
|
||||
result_cache=OBSERVATORY_PORTABLE_RESULT_CACHE,
|
||||
)
|
||||
OBSERVATORY_PORTABLE_SETUP_PROJECTOR_ERROR = None
|
||||
except (
|
||||
|
||||
@@ -33,6 +33,7 @@ from k1link.observatory.portable_queue_binding import (
|
||||
PortableQueueBindingStaleCheckError,
|
||||
PortableRecordedQueueBindingService,
|
||||
)
|
||||
from k1link.observatory.portable_result_cache import PortableResultCacheCheckRequired
|
||||
from k1link.observatory.portable_result_contract import PortableResultPublisherError
|
||||
from k1link.observatory.portable_result_publisher import (
|
||||
PortableObservatoryResultPublisher,
|
||||
@@ -298,6 +299,29 @@ def build_observatory_router(
|
||||
)
|
||||
compatible = compatibility.get("compatible") is True
|
||||
executor_ready = executor.get("state") == "ready" and executor.get("ready") is True
|
||||
projected_preflight = projected.get("preflight")
|
||||
if (
|
||||
isinstance(projected_preflight, dict)
|
||||
and projected_preflight.get("outcome") == "existing"
|
||||
):
|
||||
return {
|
||||
"schema_version": OBSERVATORY_RUN_PREFLIGHT_SCHEMA,
|
||||
"source_session_id": request.source_session_id,
|
||||
"setup_id": request.setup_id,
|
||||
"definition_sha256": expected_digest,
|
||||
"check_sha256": None,
|
||||
"outcome": "existing",
|
||||
"submission_allowed": False,
|
||||
"checks": [{
|
||||
"check_id": "published-result",
|
||||
"outcome": "pass",
|
||||
"reason_code": "exact-published-result-verified",
|
||||
"message": "Точный расчёт сохранён; повторный inference не требуется.",
|
||||
}],
|
||||
"existing_result_ids": projected_preflight["existing_result_ids"],
|
||||
"executor": executor,
|
||||
"authority": projected.get("authority", dict(_OBSERVATION_ONLY_AUTHORITY)),
|
||||
}
|
||||
checked = None
|
||||
check_reason: str | None = None
|
||||
if (
|
||||
@@ -895,6 +919,11 @@ def build_observatory_router(
|
||||
status_code=409,
|
||||
detail="Идентичность RunDefinition изменилась; повторите preflight.",
|
||||
)
|
||||
if portable_projection.get("existing_results"):
|
||||
raise HTTPException(
|
||||
status_code=409,
|
||||
detail="Точный расчёт уже сохранён. Обновите каталог и откройте результат.",
|
||||
)
|
||||
if (
|
||||
projected_executor.get("state") != "ready"
|
||||
or projected_executor.get("ready") is not True
|
||||
@@ -930,11 +959,16 @@ def build_observatory_router(
|
||||
status_code=409,
|
||||
detail="Portable-привязка источника не прошла проверку целостности.",
|
||||
) from exc
|
||||
except PortableResultCacheCheckRequired as exc:
|
||||
raise HTTPException(
|
||||
status_code=409,
|
||||
detail="Опубликованный результат требует проверки. Обновите каталог.",
|
||||
) from exc
|
||||
except ObservatoryRecordedQueueDuplicateError as exc:
|
||||
raise HTTPException(
|
||||
status_code=409,
|
||||
detail=(
|
||||
"Такой расчёт уже выполняется или ожидает публикации. "
|
||||
"Такой расчёт уже выполняется, ожидает публикации или сохранён. "
|
||||
"Обновите список расчётов; повторный запуск не создан."
|
||||
),
|
||||
) from exc
|
||||
|
||||
Reference in New Issue
Block a user