Documentação

Tratamento de erros

Os hooks rodam dentro da solicitação que observam, então como suas falhas são tratadas importa. Esta página é a política exata.

As duas políticas

hooks_raise controla o que acontece quando um hook lança.

hooks_raise comportamento
True (padrão) a exceção do hook se propaga para fora da chamada.
False o hook é ignorado com um RuntimeWarning e a chamada continua.

Ele é definido por instância e pode ser sobrescrito por chamada (hooks_raise= em predict_batch, system_one, Router.route, Router.predict). Um None por chamada significa “usar o valor da instância”.

# 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. Qualquer coisa que não seja uma Exception (veja BaseException) nunca é engolida, mesmo com hooks_raise=False.

O que roda quando algo falha

O ciclo de vida da predição é envolvido em try / except / finally, então os hooks de limpeza rodam em caso de falha.

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 falhas, por evento e runtime:

evento runtime se ele lançar
on_predict_start Agent / Router hooks_raise=True: on_error e on_predict_end ainda rodam, depois a exceção se propaga. False: avisa e continua (as mutações feitas antes de lançar permanecem).
inferência Agent / Router ctx.error definido, on_error roda, on_predict_end roda, exceção se propaga.
on_error Agent / Router nunca mascara a exceção original; encadeado como __context__.
on_predict_end (caminho de sucesso) Agent / Router hooks_raise=True: se propaga (o resultado é calculado mas a chamada falha). False: avisa.
on_predict_end (caminho de falha) Agent / Router nunca mascara a exceção original; encadeado como __context__.
on_route Router se propaga diretamente; ainda não há contexto de predição.
on_load Router se propaga diretamente; o checkpoint continua construído e residente.
on_evict Router se propaga diretamente; o checkpoint já foi liberado.

Consequências que vale conhecer:

  • Um on_load que falha deixa o modelo em cache, então o próximo load o retorna sem disparar on_load de novo.
  • Um on_predict_end que falha no caminho de sucesso significa que o chamador recebe uma exceção em vez de um resultado, mesmo que a inferência tenha tido sucesso. Use hooks_raise=False para hooks de fim que são puros efeitos colaterais.

Encadeamento de exceções

Quando um hook falha enquanto outra exceção já está se propagando, a exceção original é relançada e a exceção do hook é anexada como __context__. A causa raiz nunca é perdida.

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

A mesma regra se aplica a um on_predict_end que falha no caminho de falha.

BaseException

dispatch captura Exception, não BaseException, então KeyboardInterrupt e SystemExit sempre se propagam. Eles ainda disparam o branch except BaseException do ciclo de vida da predição, o que significa que on_error e on_predict_end rodam antes de o processo se desenrolar. Mantenha esses hooks rápidos e não bloqueantes se você se importa com a latência de interrupção.

Erros de configuração

Configuração ruim falha rápido com TypeError, antes de qualquer inferência:

caso lançado em exemplo
classe em vez de instância construção hooks=[MyHook]
nenhum método de ciclo de vida construção hooks=[object()]
evento não chamável construção on_predict_start = 5
hook de conveniência não chamável construção on_predict_start=123
callable simples em hooks= construção hooks=[lambda ctx: None]

Os hooks por chamada são validados quando a chamada é feita, então um hook por chamada ruim lança TypeError de predict/system_one em vez de na construção.

Avisos

Com hooks_raise=False, cada hook que falha emite um RuntimeWarning nomeando o tipo do hook e o evento:

laya: hook Metrics.on_predict_end failed: connection reset

O aviso é emitido uma vez por falha, não uma vez por definição de hook, então um hook instável sob carga pode ser ruidoso. Agregue ou limite a taxa dentro do hook se isso importar.

Timeouts

hooks_timeout limita cada chamada de hook em segundos. Um hook ainda rodando depois do limite é tratado como falha do hook: TimeoutError quando hooks_raise=True, um RuntimeWarning quando False. None (o padrão) significa sem limite.

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

Ele pode ser definido por instância ou sobrescrito por chamada em predict_batch, system_one, Router.route, Router.predict e ONNXAgent.system_one. O valor deve ser positivo; 0 ou um número negativo lança ValueError no ponto em que é definido, em vez de disputar em um join de duração zero.

Um hook cronometrado roda em uma thread de worker em uma cópia do contexto contextvars do chamador, então um id de solicitação ou span de tracing definido pelo chamador fica visível ao hook.

Uma ressalva honesta: o Python não consegue interromper uma thread, então um hook que estourou o tempo continua rodando em segundo plano. O timeout limita quanto tempo a solicitação espera, não quanto tempo o hook vive. Use-o para manter uma solicitação servida responsiva, não para recuperar o trabalho. Para um hook que pode travar, dê também à chamada subjacente seu próprio timeout (um timeout de socket ou HTTP). Como a thread não pode ser recuperada, um hook que trava em toda chamada acumula threads uma por chamada; dê a um hook que pode travar seu próprio limite em vez de confiar em hooks_timeout para pará-lo.

Para um hook assíncrono, a corrotina roda no event loop; um timeout do lado que chama ainda retorna depois do limite, e a corrotina continua rodando no loop.

O timeout também libera o lock de hooks_concurrent=False: o dispatch espera pelo hook apenas até o limite, depois segue em frente, enquanto o hook que estourou o tempo continua rodando fora do lock. Então o lock serializa os hooks que terminam a tempo, não todo hook que já foi iniciado; um hook que ultrapassa o limite não bloqueia mais os que estão atrás dele.

Escolher uma política

tipo de hook recomendado por quê
política / guardrail / censura hooks_raise=True uma política que falha em silêncio é um buraco de segurança.
auditoria / logging hooks_raise=True em testes, muitas vezes False em produção perder um registro de auditoria deveria ser barulhento, mas não necessariamente fatal.
métricas / tracing hooks_raise=False observabilidade não deve falhar a solicitação.
leitura/escrita de cache hooks_raise=True um cache quebrado deveria aparecer, não errar em silêncio.

Você pode misturar: instale um guardrail com o padrão da instância e dê a um hook de telemetria seu próprio try/except, ou use um hooks_raise separado por chamada.

Veja também