Ë
    tPêjˆM  ã                   ó"  — U d dl mZmZ d dlmZmZmZ d dlmZ d dl	m
Z
mZmZ d dlmZ dZeed<    G d„ d	«      Zd
Zeed<   dZeed<   dededefd„Z ed«      Zeed<   ddddœdedeg ee   f   dedeee   df   dz  dedz  dedz  defd„Zy)é    )Ú	AwaitableÚCallable)ÚAnyÚFinalÚTypeVar)Úverbose_proxy_logger)ÚDB_CONNECTION_ERROR_TYPESÚProxyErrorTypesÚProxyException)Ústr_to_boolé   Ú_MAX_EXCEPTION_CHAIN_DEPTHc                   ó  — e Zd ZdZedefd„«       Zededefd„«       Zededefd„«       Z	ededefd„«       Z
ededefd„«       Zededefd	„«       Zededefd
„«       Zededefd„«       Zededefd„«       Zedefd„«       Zy)ÚPrismaDBExceptionHandlerz<
    Class to handle DB Exceptions or Connection Errors
    Úreturnc                  ót   — ddl m}  | j                  dd«      }t        |t        «      r|S t        |«      du ryy)zj
        Returns True if the request should be allowed to proceed despite the DB connection error
        r   )Úgeneral_settingsÚ allow_requests_on_db_unavailableFT)Úlitellm.proxy.proxy_serverr   ÚgetÚ
isinstanceÚboolr   )r   Ú!_allow_requests_on_db_unavailables     úg/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/proxy/db/exception_handler.pyÚ&should_allow_request_on_db_unavailablez?PrismaDBExceptionHandler.should_allow_request_on_db_unavailable   sB   € õ
 	@à8H×8LÑ8LÐMoÐqvÓ8wÐ)ÜÐ7¼Ô>Ø4Ð4ÜÐ8Ó9¸TÑAØØó    Úec                 óà   — ddl }t        | t        «      ryt        | |j                  j                  j
                  «      ryt        | t        «      xr | j                  t        j                  k(  S )a(  True only for a database that is temporarily unreachable and is
        expected to come back on its own.

        This is the gate for ``allow_requests_on_db_unavailable``, which lets
        the proxy keep serving, and issue fallback identities, without a
        verified database. Only a transient outage justifies that. A fault that
        will never resolve by itself, such as a query engine that is missing or
        version-skewed, a malformed query the client library built, or a
        transaction used incorrectly, must surface rather than be absorbed into
        an indefinite degraded mode.

        Membership is an allowlist, so an unrecognized failure is treated as
        permanent. A genuine outage reaches the caller as one of
        ``DB_CONNECTION_ERROR_TYPES``: the engine is a local HTTP server, and an
        unreachable database surfaces as a transport error against it rather
        than as a prisma type.

        Reporting decisions want the opposite breadth; use
        ``is_database_infrastructure_error`` for those.
        r   NT)
Úprisma.engine.errorsr   r	   ÚengineÚerrorsÚEngineConnectionErrorr   Útyper
   Úno_db_connection©r   Úprismas     r   Úis_database_connection_errorz5PrismaDBExceptionHandler.is_database_connection_error$   sS   € ó, 	$ä�aÔ2Ô3ØÜ�a˜Ÿ™×-Ñ-×CÑCÔDØÜ˜!œ^Ó,Ò[°·±¼?×;[Ñ;[Ñ1[Ð[r   c                 ó  — ddl }|j                  j                  |j                  j                  |j                  j                  |j                  j
                  |j                  j                  |j                  j                  |j                  j                  f}t        | |«      ryt        | t        «      ryt        | |j                  j                  «      ryt        | t        «      r| j                  t        j                  k(  ryy)uù  True when the failure came from the database or its query engine
        rather than from the caller's request.

        This answers a reporting question, not a serving one: should the caller
        be told the service is at fault, or that their request was. It stays
        deliberately broad, because a permanently faulted engine is still a
        service problem, and reporting one as a credential failure sends an
        operator looking in the wrong place. Widening it can only change which
        error a caller sees; it never grants access.

        Known data-layer PrismaError subclasses (``UniqueViolationError``,
        ``RecordNotFoundError``, etc.) are excluded â€” the DB IS reachable and
        the request itself is what failed.
        r   NFT)r&   r!   Ú	DataErrorÚUniqueViolationErrorÚForeignKeyViolationErrorÚMissingRequiredValueErrorÚRawQueryErrorÚTableNotFoundErrorÚRecordNotFoundErrorr   r	   ÚPrismaErrorr   r#   r
   r$   )r   r&   Údata_layer_errorss      r   Ú is_database_infrastructure_errorz9PrismaDBExceptionHandler.is_database_infrastructure_errorB   sÂ   € ó  	ð �M‰M×#Ñ#Ø�M‰M×.Ñ.Ø�M‰M×2Ñ2Ø�M‰M×3Ñ3Ø�M‰M×'Ñ'Ø�M‰M×,Ñ,Ø�M‰M×-Ñ-ð$
Ðô �aÐ*Ô+ØÜ�aÔ2Ô3ØÜ�a˜Ÿ™×2Ñ2Ô3ØÜ�aœÔ(¨Q¯V©V´×7WÑ7WÒ-WØØr   c                 óL   — ddl }t        | «      |j                  j                  u S )aê  True iff ``e`` is a base prisma ``DataError``: the database processed
        the statement and refused the data itself (e.g. ``invalid byte sequence
        for encoding "UTF8": 0x00``), as opposed to a connectivity failure.

        Matched by exact type, not ``isinstance``: the specific data-layer
        subclasses (``UniqueViolationError``, ``TableNotFoundError``,
        ``MissingRequiredValueError`` ...) all derive from ``DataError`` but
        carry their own semantics, and a systemic one like a missing table must
        not be mistaken for a single poison row and bisected away. A raw
        Postgres execution error with no prisma P-code surfaces as the base
        ``DataError``.

        prisma also wraps the P1001 "can't reach database server" outage as a
        base ``DataError``, so a caller that must not treat an outage as a
        per-row data rejection has to additionally consult
        ``is_database_service_unavailable_error`` before acting on a True here.
        r   N)r&   r#   r!   r)   r%   s     r   Úis_prisma_data_errorz-PrismaDBExceptionHandler.is_prisma_data_errorg   s    € ó& 	ä�A‹w˜&Ÿ-™-×1Ñ1Ð1Ð1r   c                 óš  ‡— ddl }t        | t        «      ryt        | |j                  j                  |j                  j
                  f«      ryt        | |j                  j                  «      r0t        | «      j                  «       Šd}t        ˆfd„|D «       «      ryt        | t        «      r| j                  t        j                  k(  ryy)u?  
        Returns True only for transport/connectivity failures where a reconnect
        attempt makes sense (e.g. DB is unreachable, connection dropped).

        Use this for reconnect logic â€” data-layer errors like UniqueViolationError
        mean the DB IS reachable, so reconnecting would be pointless.
        r   NT)zcan't reach database serverzcannot reach database serverzcan't connectzcannot connectzconnection errorzconnection closedz	timed outÚtimeoutzconnection refusedznetwork is unreachablezno route to hostzbroken pipec              3   ó&   •K  — | ]  }|‰v –— Œ
 y ­w)N© )Ú.0ÚkeywordÚerror_messages     €r   ú	<genexpr>zGPrismaDBExceptionHandler.is_database_transport_error.<locals>.<genexpr>£   s   øè ø€ ÒO°�7˜mÔ+ÑOùs   ƒF)r&   r   r	   r!   ÚClientNotConnectedErrorÚHTTPClientClosedErrorr0   ÚstrÚlowerÚanyr   r#   r
   r$   )r   r&   Úconnection_keywordsr;   s      @r   Úis_database_transport_errorz4PrismaDBExceptionHandler.is_database_transport_error~   s¥   ø€ ó 	ä�aÔ2Ô3ØÜØà—‘×5Ñ5Ø—‘×3Ñ3ðô
ð Ü�a˜Ÿ™×2Ñ2Ô3Ü#& q£6§<¡<£>ˆMð*Ðô ÓOÐ;NÔOÔOØÜ�aœÔ(¨Q¯V©V´×7WÑ7WÒ-WØØr   c                 óÀ   — ddl }t        | |j                  j                  «      syt	        | dd«      dk(  ryt        | «      j                  «       }d|v xs
 d|v xs d	|v S )
zNTrue iff ``e`` is a Postgres deadlock (P2034 / 40P01) surfaced through prisma.r   NFÚcodeÚP2034Tzdeadlock detectedÚ40p01zwrite conflict or a deadlock)r&   r   r!   r0   Úgetattrr?   r@   )r   r&   r;   s      r   Úis_deadlock_errorz*PrismaDBExceptionHandler.is_deadlock_error©   sh   € ó 	ä˜!˜VŸ]™]×6Ñ6Ô7ØÜ�1�f˜dÓ# wÒ.ØÜ˜A›Ÿ™›ˆà =Ð0ò ?Ø˜-Ð'ò?à-°Ð>ð	
r   c                 óö   — ddl }t        | |j                  j                  «      ryt	        | dd«      }|�E|j
                  j                  j                  dd«      j                  d«      ry|j                  }|�ŒEy)	af  True iff ``e`` is a non-``PrismaError`` exception raised from inside
        prisma-client-py's query-engine layer.

        During the instant a DB connection is torn down, the query engine can
        return a malformed error payload (``user_facing_error.meta`` is
        ``null``). prisma-client-py's ``handle_response_errors`` then crashes
        with ``AttributeError: 'NoneType' object has no attribute 'get'``
        before it can raise the proper P1001 "can't reach database server"
        error. That AttributeError carries no connection keyword, so it can't
        be matched by message; identify it by its ``prisma.engine`` origin
        instead.

        Recognized ``PrismaError`` subclasses are excluded: connectivity ones
        are already classified by type/keyword above, and data-layer ones
        (the DB IS reachable) must stay 401.
        r   NFÚ__traceback__Ú__name__Ú zprisma.engineT)
r&   r   r!   r0   rH   Útb_frameÚ	f_globalsr   Ú
startswithÚtb_next)r   r&   Útbs      r   Úis_prisma_engine_internal_errorz8PrismaDBExceptionHandler.is_prisma_engine_internal_error¹   sn   € ó$ 	ä�a˜Ÿ™×2Ñ2Ô3ØÜ�Q˜¨Ó.ˆØˆnØ�{‰{×$Ñ$×(Ñ(¨°RÓ8×CÑCÀOÔTØØ—‘ˆBð ‰nð r   c                 ó”  — ddl }t        j                  | «      ryt        j                  | «      ryt        j	                  | «      rydt        | «      j                  «       v ryt        | t        |j                  f«      ry	 ddl
}t        | |j                  j                  |j                  j                  f«      S # t        $ r Y yw xY w)aß  True iff the exception means the database could not answer at the
        infrastructure level (connection refused, socket/interface failure,
        timeout) rather than a genuine auth failure (key not found) or a
        data-layer error (the DB IS reachable and rejected the data).

        Auth must answer 401 only for a key the DB confirms is invalid. When
        the DB itself is unreachable, the request has to surface as 503 so
        callers retry instead of treating valid keys as invalid during an
        outage.

        Note: prisma-client-py mislabels the P1001 "can't reach database
        server" connectivity failure as a ``DataError`` (a data-layer type),
        so a type-only check misses real outages. ``is_database_transport_error``
        keyword-matches the connection message and catches that masquerade,
        while genuine data errors (no connection keyword) correctly stay 401.

        The Postgres "cached plan must not change result type" error is matched
        here, not in ``is_database_transport_error``: it is a transient stale-DB-
        state condition (not an invalid key), but the connection is healthy so it
        must not trigger a reconnect.

        A non-``PrismaError`` raised from inside the prisma query engine (e.g.
        the ``AttributeError`` from ``handle_response_errors`` when the engine
        returns a malformed error payload mid-tear-down) is also treated as
        unavailable; see ``is_prisma_engine_internal_error``.
        r   NTz'cached plan must not change result typeF)Úasyncior   r2   rC   rS   r?   r@   r   ÚOSErrorÚTimeoutErrorÚasyncpgÚImportErrorÚ
exceptionsÚPostgresConnectionErrorÚInterfaceError)r   rU   rX   s      r   Ú%is_database_service_unavailable_errorz>PrismaDBExceptionHandler.is_database_service_unavailable_errorÖ   sº   € ó8 	ä#×DÑDÀQÔGØÜ#×?Ñ?ÀÔBØÜ#×CÑCÀAÔFØØ4¼¸A»¿¹»ÑFØô �aœ' 7×#7Ñ#7Ð8Ô9Øð	Ûô Øà×"Ñ"×:Ñ:Ø×"Ñ"×1Ñ1ðó
ð 	
øô ò 	Ùð	ús   ÂB; Â;	CÃCc                 ó¶   — | }t        t        «      D ]E  }t        |t        «      s yt        j                  |«      r y|j                  xs |j                  }ŒG y)az  Like ``is_database_service_unavailable_error`` but also walks the
        ``__cause__`` / ``__context__`` chain.

        ``is_database_service_unavailable_error`` classifies a single exception
        by type, which a caller that catches a raw DB failure and re-raises a
        domain exception of a different type defeats. ``get_user_object`` in
        ``litellm/proxy/auth/auth_checks.py`` is the concrete case: it wraps
        every DB error, a genuine outage included, in a bare ``ValueError``
        whose original error survives only as ``__context__``. A type check on
        the ``ValueError`` misses the outage, so the caller would mistake an
        infrastructure fault for an auth failure. Walking the chain recovers the
        real signal, which is the PEP 3134 way to inspect a wrapped cause.

        The walk is depth-bounded, which also makes it cycle-safe.
        FT)Úranger   r   Ú	Exceptionr   r]   Ú	__cause__Ú__context__)r   ÚcurrentÚ_s      r   Ú.is_database_service_unavailable_error_in_chainzGPrismaDBExceptionHandler.is_database_service_unavailable_error_in_chain  sW   € ð" )*ˆÜÔ1Ó2ò 	?ˆAÜ˜g¤yÔ1ÙÜ'×MÑMÈgÔVÙØ×'Ñ'Ò>¨7×+>Ñ+>‰Gð	?ð r   c                 óZ   — t         j                  | «      rt         j                  «       ry| ‚)aK  
        Primary handler for `allow_requests_on_db_unavailable` flag. Decides whether to raise a DB Exception or not based on the flag.

        - If exception is a DB Connection Error, and `allow_requests_on_db_unavailable` is True,
            - Do not raise an exception, return None
        - Else, raise the exception
        N)r   r'   r   )r   s    r   Úhandle_db_exceptionz,PrismaDBExceptionHandler.handle_db_exception)  s&   € ô %×AÑAÀ!ÔDÜ(×OÑOÔQàØˆr   N)rL   Ú
__module__Ú__qualname__Ú__doc__Ústaticmethodr   r   r`   r'   r2   r4   rC   rI   rS   r]   ÚBaseExceptionre   rg   r8   r   r   r   r      sT  „ ñð ð°Dò ó ðð ð\¨	ð \°dò \ó ð\ð: ð"¨Ið "¸$ò "ó ð"ðH ð2 	ð 2¨dò 2ó ð2ð, ð( yð (°Tò (ó ð(ðT ð
˜Yð 
¨4ò 
ó ð
ð ð¨9ð ¸ò ó ðð8 ð6
°ð 6
¸tò 6
ó ð6
ðp ð¸-ð ÈDò ó ðð2 ð˜yò ó ñr   r   g       @Ú"_DEFAULT_RECONNECT_TIMEOUT_SECONDSgš™™™™™¹?Ú'_DEFAULT_RECONNECT_LOCK_TIMEOUT_SECONDSÚvalueÚfallbackr   c                 óh   — t        | t        t        f«      rt        | t        «      st        | «      S |S )z¥Return `value` if it is a real int/float, else `fallback`. Guards
    against tests that mock `prisma_client` and leave the timeout slots as
    MagicMock instances.)r   ÚintÚfloatr   )ro   rp   s     r   Ú_coerce_timeoutrt   A  s*   € ô �%œ#œu˜Ô&¬z¸%ÄÔ/FÜ�U‹|ÐØ€Or   Ú_ReadResultTN)Úretry_safe_error_typesÚtimeout_secondsÚlock_timeout_secondsÚprisma_clientÚcoro_factoryÚreasonrv   .rw   rx   c             ƒ   ó"  K  — 	  |«       ƒ d{  –—† S 7 Œ# t         $ rî}t        j                  |«      s‚ |�t        ||«      s‚ t	        | d«      s‚ t        |�|nt        | dd«      t        «      }t        |�|nt        | dd«      t        «      }t        j                  d||«       	 | j                  |||¬«      ƒ d{  –—†7  }	n+# t         $ r}
t        j                  d||
«       ||
‚d}
~
ww xY w|	s‚  |«       ƒ d{  –—†7  cY d}~S d}~ww xY w­w)u9  Run a Prisma read coroutine with one transport-reconnect-and-retry.

    The canonical "self-heal a transient DB transport blip" wrapper used by
    `PrismaClient.get_generic_data` and other read paths. Mirrors the inline
    pattern in `auth_checks._fetch_key_object_from_db_with_reconnect` so we
    have a single implementation rather than three drifting copies.

    Behavior:
      1. Await `coro_factory()`. On success, return its value.
      2. On exception, if it is NOT a transport error (per
         `is_database_transport_error`), re-raise â€” data-layer errors like
         `UniqueViolationError` mean the DB is reachable, reconnect would be
         pointless. Transport errors outside `retry_safe_error_types` are
         re-raised too.
      3. If `prisma_client` does not expose `attempt_db_reconnect`, re-raise.
         This guards against partial stand-ins / older clients in tests.
      4. Call `prisma_client.attempt_db_reconnect(reason=...)`. If it returns
         False (cooldown / lock contention / reconnect failure), re-raise.
      5. Otherwise await `coro_factory()` a second time and return / propagate
         its result. At-most-one retry by construction â€” no infinite loop.

    `coro_factory` MUST be a zero-arg callable that returns a fresh awaitable
    on each call. Passing an already-awaited coroutine would fail on retry
    with `RuntimeError: cannot reuse already awaited coroutine`.

    `reason` should follow `<subsystem>_<operation>_<table>_failure` so
    telemetry distinguishes between fan-out callers (e.g.
    `_update_config_from_db` issues four concurrent reads).

    Args:
        prisma_client: The `PrismaClient` (or stand-in) that owns
            `attempt_db_reconnect` and the `_db_auth_reconnect_*` defaults.
        coro_factory: Zero-arg callable returning the read awaitable.
        reason: Telemetry tag forwarded to `attempt_db_reconnect`.
        retry_safe_error_types: Which transport errors may be replayed, or
            None for every transport error. A non-idempotent write must narrow
            this to `DB_RETRY_SAFE_ERROR_TYPES`, where the statements provably
            never reached the database.
        timeout_seconds: Optional override for the reconnect cycle timeout.
            Defaults to `prisma_client._db_auth_reconnect_timeout_seconds`,
            then to 2.0s.
        lock_timeout_seconds: Optional override for how long the helper will
            wait to acquire the reconnect lock. Defaults to
            `prisma_client._db_auth_reconnect_lock_timeout_seconds`, then to
            0.1s.

    Returns:
        Whatever `coro_factory()` returns (on first or second attempt).

    Raises:
        Whatever `coro_factory()` raises if the failure is not a transport
        error, or if the reconnect attempt does not succeed, or if the retry
        also fails.
    NÚattempt_db_reconnectÚ"_db_auth_reconnect_timeout_secondsÚ'_db_auth_reconnect_lock_timeout_secondszNDB transport error on read; attempting reconnect-and-retry. reason=%s error=%s)r{   rw   rx   z^DB reconnect attempt raised; preserving original transport error. reason=%s reconnect_error=%s)r`   r   rC   r   Úhasattrrt   rH   rm   rn   r   Úwarningr}   )ry   rz   r{   rv   rw   rx   Ú	first_excÚresolved_timeoutÚresolved_lock_timeoutÚdid_reconnectÚreconnect_excs              r   Úcall_with_db_reconnect_retryr‡   M  sB  è ø€ ð~:$Ù!“^×#Ð#Ð#ùÜò 8$Ü'×CÑCÀIÔNØØ!Ð-´jÀÐLbÔ6cØÜ�}Ð&<Ô=Øä"1ð #Ð.ñ  ä˜]Ð,PÐRVÓWä.ó#
Ðô (7ð (Ð3ñ %ä˜]Ð,UÐW[Ó\ä3ó(
Ðô 	×$Ñ$Ø\ØØô	
ð	/Ø)6×)KÑ)KØØ 0Ø%:ð *Ló *÷ $ð $‰Møô
 ò 	/Ü ×(Ñ(ØpØØôð
  Ð.ûð	/úñ Øñ
 "“^×#Ð#Õ#ûðq8$üsy   ‚D„
 Ž� ’D“ •	DžBDÂ&CÂ=C Â>CÃDÃ	C,ÃC'Ã'C,Ã,DÃ<C?Ã=DÄDÄDÄDÄD)Úcollections.abcr   r   Útypingr   r   r   Úlitellm._loggingr   Úlitellm.proxy._typesr	   r
   r   Úlitellm.secret_managers.mainr   r   Ú__annotations__r   rm   rn   rs   rt   ru   r?   Útupler#   r`   r‡   r8   r   r   ú<module>r�      s  ðß /Ð /ß &Ñ &å 1÷ñ õ
 5ð %'Ð ˜EÓ &÷fñ fðX	 -0Ð " EÓ /Ø14Ð '¨Ó 4ð˜3ð ¨%ð °Eó ñ ˜nÓ-€ˆeÓ -ð BFØ$(Ø)-òy$Øðy$à˜2˜y¨Ñ6Ð6Ñ7ðy$ð ð	y$ð
 " $ y¡/°3Ð"6Ñ7¸$Ñ>ðy$ð ˜T‘\ðy$ð   $™,ðy$ð ôy$r   