Documentación

Manejo de errores

Los hooks se ejecutan dentro de la solicitud que observan, así que importa cómo se manejan sus fallos. Esta página describe la política exacta.

Las dos políticas

hooks_raise controla qué ocurre cuando un hook lanza una excepción.

hooks_raise comportamiento
True (predeterminado) la excepción del hook se propaga fuera de la llamada.
False el hook se omite con un RuntimeWarning y la llamada continúa.

Se define por instancia y se puede sobrescribir por llamada (hooks_raise= en predict_batch, system_one, Router.route, Router.predict). Un None por llamada significa “usar el valor de la instancia”.

# strict: a broken audit hook fails the request
laya.load("convaiinnovations/laya", on_predict_end=audit, hooks_raise=True)

# lenient: telemetry must never take down a served request
laya.load("convaiinnovations/laya", on_predict_end=metrics, hooks_raise=False)

dispatch captura Exception. Todo lo que no sea una Exception (consulta BaseException) nunca se silencia, ni siquiera con hooks_raise=False.

Qué se ejecuta cuando algo falla

El ciclo de vida de predict está envuelto en try / except / finally, así que los hooks de limpieza se ejecutan cuando hay un fallo.

try:
    on_predict_start
    inference
except BaseException as exc:
    ctx.error = exc
    on_error            (best effort; cannot mask exc)
    raise
finally:
    elapsed_ms, usage
    on_predict_end      (best effort; cannot mask exc on the failure path)

Matriz de fallos, por evento y runtime:

evento runtime si lanza una excepción
on_predict_start Agent / Router hooks_raise=True: on_error y on_predict_end todavía se ejecutan, y luego la excepción se propaga. False: avisa y continúa (las mutaciones hechas antes del lanzamiento se mantienen).
inference Agent / Router ctx.error se establece, on_error se ejecuta, on_predict_end se ejecuta, la excepción se propaga.
on_error Agent / Router nunca enmascara la excepción original; se encadena como __context__.
on_predict_end (ruta de éxito) Agent / Router hooks_raise=True: se propaga (el resultado se calcula, pero la llamada falla). False: avisa.
on_predict_end (ruta de fallo) Agent / Router nunca enmascara la excepción original; se encadena como __context__.
on_route Router se propaga directamente; todavía no hay un contexto de predict.
on_load Router se propaga directamente; el checkpoint permanece construido y residente.
on_evict Router se propaga directamente; el checkpoint ya se ha liberado.

Consecuencias que conviene conocer:

  • Un on_load que falla deja el modelo en caché, así que el siguiente load lo devuelve sin volver a disparar on_load.
  • Un on_predict_end que falla en la ruta de éxito significa que quien llama recibe una excepción en lugar de un resultado, aunque la inferencia haya tenido éxito. Usa hooks_raise=False para los hooks finales que son efectos secundarios puros.

Encadenamiento de excepciones

Cuando un hook falla mientras ya se está propagando otra excepción, la excepción original se vuelve a lanzar y la del hook se adjunta como __context__. La causa raíz nunca se pierde.

class BadTelemetry:
    def on_error(self, ctx):
        raise RuntimeError("telemetry down")

try:
    agent.system_one(state, questions, hooks=[BadTelemetry()])
except RuntimeError as exc:
    assert exc.__context__ is not None   # the telemetry failure

La misma regla se aplica a un on_predict_end que falla en la ruta de fallo.

BaseException

dispatch captura Exception, no BaseException, así que KeyboardInterrupt y SystemExit siempre se propagan. Aun así, activan la rama except BaseException del ciclo de vida de predict, lo que significa que on_error y on_predict_end se ejecutan antes de que el proceso se desmonte. Mantén esos hooks rápidos y sin bloqueos si te importa la latencia de interrupción.

Errores de configuración

Una configuración incorrecta falla rápido con TypeError, antes de cualquier inferencia:

caso se lanza en ejemplo
clase en lugar de instancia construcción hooks=[MyHook]
sin método de ciclo de vida construcción hooks=[object()]
evento no invocable construcción on_predict_start = 5
hook de conveniencia no invocable construcción on_predict_start=123
invocable simple en hooks= construcción hooks=[lambda ctx: None]

Los hooks por llamada se validan cuando se hace la llamada, así que un hook por llamada incorrecto lanza TypeError desde predict/system_one en lugar de en la construcción.

Advertencias

Con hooks_raise=False, cada hook que falla emite un RuntimeWarning que nombra el tipo de hook y el evento:

laya: hook Metrics.on_predict_end failed: connection reset

La advertencia se emite una vez por fallo, no una vez por definición de hook, así que un hook inestable bajo carga puede ser ruidoso. Si eso importa, agrega o limita la tasa dentro del hook.

Tiempos de espera

hooks_timeout limita cada llamada de hook en segundos. Un hook que sigue ejecutándose después del límite se trata como un fallo de hook: TimeoutError cuando hooks_raise=True, un RuntimeWarning cuando False. None (el valor predeterminado) significa sin límite.

laya.load("convaiinnovations/laya", on_predict_end=metrics, hooks_timeout=2.0)

Se puede definir por instancia o sobrescribir por llamada en predict_batch, system_one, Router.route, Router.predict y ONNXAgent.system_one. El valor debe ser positivo; 0 o un número negativo lanza ValueError en el punto en que se define, en lugar de competir en un join de longitud cero.

Un hook con tiempo límite se ejecuta en un hilo de trabajo sobre una copia del contexto contextvars de quien llama, así que un id de solicitud o un span de tracing definido por quien llama es visible para el hook.

Una advertencia honesta: Python no puede interrumpir un hilo, así que un hook que agota el tiempo sigue ejecutándose en segundo plano. El tiempo límite acota cuánto espera la solicitud, no cuánto vive el hook. Úsalo para mantener receptiva una solicitud servida, no para recuperar el trabajo. Para un hook que puede colgarse, dale también su propio tiempo límite a la llamada subyacente (un tiempo límite de socket o HTTP). Como el hilo no se puede recuperar, un hook que se cuelga en cada llamada acumula hilos uno por llamada; dale a un hook que puede colgarse su propio límite en lugar de confiar en que hooks_timeout lo detenga.

Para un hook asíncrono, la corrutina se ejecuta en el bucle de eventos; un tiempo límite en el lado que llama igualmente devuelve después del límite, y la corrutina sigue ejecutándose en el bucle.

El tiempo límite también libera el bloqueo de hooks_concurrent=False: dispatch espera al hook solo hasta el límite y luego sigue, mientras que el hook que agotó el tiempo sigue ejecutándose fuera del bloqueo. Así, el bloqueo serializa los hooks que terminan a tiempo, no todos los hooks que se iniciaron alguna vez; un hook que se pasa de tiempo ya no bloquea a los que vienen detrás.

Elección de una política

tipo de hook recomendado por qué
política / guardarraíl / ocultación de datos hooks_raise=True una política que falla en silencio es un agujero de seguridad.
auditoría / registro hooks_raise=True en pruebas, a menudo False en producción perder un registro de auditoría debería ser ruidoso, pero no necesariamente fatal.
métricas / tracing hooks_raise=False la observabilidad no debe hacer fallar la solicitud.
lectura/escritura de caché hooks_raise=True una caché rota debería aflorar, no fallar en silencio.

Puedes combinar: instalar un guardarraíl con el valor predeterminado de la instancia y dar a un hook de telemetría su propio try/except, o usar un hooks_raise distinto por llamada.

Véase también