Ctrl K
ComunidadGuías…

cookbook/04_errors_and_backpressure.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# 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 []