cookbook/04_errors_and_backpressure.py
Rama main · réplica de lectura del repositorio original.
# Copyright (c) 2026 OpenNVR
# SPDX-License-Identifier: Apache-2.0
"""Failing well — `ServiceError`, `ErrorCategory`, `Overloaded`.
Demonstrates: `ServiceError`, `ErrorCategory`, `FailureEnvelope`,
`ErrorDetail`, `Overloaded`, and what the facade classifies for you.
The category is not paperwork: KAI-C routes on it. A `transport_error`
is not retried, a `model_error` counts against the adapter, an
`overloaded` makes KAI-C back off and come back. Getting this wrong
turns one bad frame into a retry storm, or a busy adapter into one the
operator is told is broken.
"""
from opennvr_adapter_sdk import Adapter, Overloaded, ServiceError
from opennvr_adapter_sdk.contract import ErrorCategory
adapter = Adapter("demo-errors", tasks=["object_detection"])
@adapter.load()
def load():
return object()
@adapter.on_image()
def infer(call):
"""What the facade does for you, and when to be explicit."""
# ── Classified for you ─────────────────────────────────────────
#
# ValueError / KeyError → 400 transport_error, not retried. The
# caller sent something this model cannot use.
# anything else (TypeError included) → 500 model_error, logged
# with a traceback: a TypeError is nearly always this
# handler's own bug, not the caller's.
# Overloaded → 503 with retry_after_ms.
if not call.image:
raise ValueError("a frame is required")
if _queue_depth() > 32:
raise Overloaded("inference queue is full", retry_after_ms=250)
# ── Be explicit when the category is not obvious ───────────────
if call.param("match_faces") and not _face_matching_permitted():
# The operator's policy, not a model failure. PERMISSION_DENIED
# tells KAI-C this will not succeed on retry either, and the
# message reaches the operator's audit log.
raise ServiceError(
ErrorCategory.PERMISSION_DENIED,
code="face_matching_refused",
message="Site policy does not permit face matching on this camera.",
transient=False,
http_status=403,
)
try:
return _run(call.model, call.image)
except ConnectionError as exc:
# An upstream this adapter fronts is down — the adapter is
# fine, its provider is not. Transient, so KAI-C retries.
raise ServiceError(
ErrorCategory.PROVIDER_ERROR,
code="upstream_unavailable",
message=f"The inference provider is unreachable: {exc}",
transient=True,
http_status=502,
retry_after_ms=5000,
) from exc
#: What each category means to the caller, in one place.
#:
#: ================== ====== ========= ===================================
#: category status retried? use it when
#: ================== ====== ========= ===================================
#: transport_error 400 no the request is malformed
#: permission_denied 403 no policy refused this call
#: not_supported 501 no this adapter does not do that
#: model_error 500 no valid input, the model failed
#: provider_error 502 yes an upstream dependency failed
#: overloaded 503 yes backpressure — come back later
#: ================== ====== ========= ===================================
CATEGORIES = tuple(ErrorCategory)
def _queue_depth() -> int:
return 0
def _face_matching_permitted() -> bool:
return True
def _run(model, frame):
return []