Ë
    yÌj=  ã                   óÀ   — U d Z ddlZddlZddlmZ  ej
                  e«      ZdZdZ	dZ
dZi Zeeef   ed<   d	ed
efd„Zdeded	ed
dfd„Zded
dfd„Zded
ee   fd„Zy)u  
Salud de los servicios de IA por tenant â€” el canal por el que un fallo de fondo
llega a la pantalla.

La generaciÃ³n anticipada y el pre-recorte de fondo los lanza el organizador, sin
nadie delante. El dÃ­a que la cuenta se quedÃ³ sin saldo, el Ãºnico rastro fueron
unas lÃ­neas de log en el servidor: en pantalla no pasaba nada, las fotos se
organizaban con normalidad y simplemente no aparecÃ­an las versiones IA. AquÃ­ se
guarda el Ãºltimo fallo definitivo de cada tenant y `/api/organizer/status` lo
devuelve junto al resto del estado â€” el front ya sondea ese endpoint, asÃ­ que no
hace falta ni un endpoint ni un sondeo mÃ¡s.

Dos reglas que definen lo que se guarda:

- **SÃ³lo fallos definitivos.** Quien registra lo hace DESPUÃ‰S de `with_retries`,
  nunca dentro. Los 5xx esporÃ¡dicos del proveedor bajo carga se resuelven solos
  en el siguiente intento; encender un aviso por cada uno enseÃ±arÃ­a a ignorarlos,
  que es justo lo que no puede pasar con el que sÃ­ importa.
- **Un Ã©xito lo borra.** El estado responde Â«Â¿funciona ahora?Â», no Â«Â¿fallÃ³ alguna
  vez?Â». Y caduca por su cuenta (`_TTL_SECS`): un fallo de hace horas del que
  nadie ha vuelto a saber no debe recibir a quien abre la aplicaciÃ³n por la
  tarde.

`code` distingue el caso que no se arregla reintentando (`no_credits`) del resto
(`error`), porque son dos acciones distintas: recargar la cuenta o mirar el log.
El texto crudo del proveedor se queda en el log y NO viaja al front: nombra al
servicio externo, y eso no tiene por quÃ© acabar en la pantalla de un cliente.
é    N)ÚOptionali  ÚiaÚnobgÚheadsÚ_stateÚexcÚreturnc                 óZ   — t        t        | d| «      «      j                  «       }d|v rdS dS )uœ   `no_credits` es el Ãºnico caso que merece su propio mensaje: el proveedor
    responde 400 y no hay reintento que lo arregle â€” hay que recargar la cuenta.Údetailzinsufficient creditsÚ
no_creditsÚerror)ÚstrÚgetattrÚlower)r   Úmsgs     ú-/var/www/photos/backend/services/ai_health.pyÚ	_classifyr   /   s2   € ô Œg�c˜8 SÓ)Ó
*×
0Ñ
0Ó
2€CØ1°SÑ8ˆ<ÐE¸gÐEó    Ú
short_codeÚtaskc                 ó²   — | syt        |«      }t        j                  | «      }|r|d   |k(  r|d   dz   nd}|||t        j                  «       dœt        | <   y)zFRegistra un fallo YA definitivo (reintentos agotados o no aplicables).NÚcodeÚcounté   )r   r   r   Úat)r   r   ÚgetÚtime)r   r   r   r   Úprevr   s         r   Úrecord_failurer   6   s\   € áØÜ�S‹>€DÜ�:‰:�jÓ!€Dñ "&¨$¨v©,¸$Ò*>ˆD�‰M˜AÒÀA€EàØØÜ�i‰i‹kñ	„Fˆ:Òr   c                 ól   — | r2t         j                  | d«      rt        j                  d| ddi¬«       yyy)uI   Una llamada que sÃ­ funcionÃ³: el servicio estÃ¡ vivo, se borra el aviso.Nu*   ðŸŸ¢ [%s] servicios IA operativos de nuevoÚauditT)Úextra)r   ÚpopÚloggerÚinfo)r   s    r   Úrecord_successr&   G   s3   € á”f—j‘j ¨TÔ2ä�‰Ð@À*ÐU\Ð^bÐTcˆÕdð 3€zr   c                 óÚ   — t         j                  | «      }|syt        j                  «       |d   z
  }|t        kD  rt         | = y|d   |d   |d   t	        |«      t	        |d   dz  «      dœS )u.  Estado para el front, o `None` si no hay nada que contar.

    Devuelve `at` en segundos de antigÃ¼edad en vez de una marca de tiempo: el
    front sÃ³lo necesita saber si es el mismo episodio del que ya avisÃ³, y una
    edad no depende de que el reloj del navegador coincida con el del servidor.
    Nr   r   r   r   iè  )r   r   r   Úseconds_agoÚepisode)r   r   r   Ú	_TTL_SECSÚint)r   ÚstÚages      r   Ú
get_statusr.   N   sv   € ô 
�‰�JÓ	€BÙØÜ
�)‰)‹+˜˜4™Ñ
 €CØ
ŒY‚Ü�:ÐØà�6‘
Ø�6‘
Ø�G‘Ü˜3“xô �r˜$‘x $‘Ó'ñð r   )Ú__doc__Úloggingr   Útypingr   Ú	getLoggerÚ__name__r$   r*   ÚTASK_IAÚ	TASK_NOBGÚ
TASK_HEADSr   Údictr   Ú__annotations__ÚBaseExceptionr   r   r&   r.   © r   r   ú<module>r;      sµ   ðòó8 Û Ý à	ˆ×	Ñ	˜8Ó	$€ð €	ð €Ø€	Ø€
à€ˆˆS�$ˆY‰Ó ðF�=ð F Só Fð˜sð ¨#ð °Mð Àdó ð"e˜sð e tó eð˜3ð  8¨D¡>ô r   