§
    ÷žyj•&  ã                  óž   — d Z ddlmZ ddlZddlZddlmZmZmZm	Z	  ej
        e¦  «        ZdZdd„Zdd„Zd d„Zddœd!d„Zddœd"d„Zg d¢ZdS )#u˜  Stream diagnostics â€” per-attempt counters, exception chains, retry logging.

When a streaming chat-completions request dies mid-response, we want to
know why: which Cloudflare edge served the request, which OpenRouter
downstream provider answered, how many bytes/chunks we got before the
drop, the HTTP status, the underlying httpx error class.  These helpers
collect that info and emit it both to ``agent.log`` (full detail) and to
the user-facing status line (compact).

All helpers are extracted from :class:`AIAgent` for cleanliness.
``run_agent`` keeps thin forwarder methods so existing call sites and
tests that patch ``run_agent.<helper>`` keep working.
é    )ÚannotationsN)ÚAnyÚDictÚListÚOptional)
zcf-rayzcf-cache-statuszx-openrouter-providerzx-openrouter-modelzx-openrouter-idzx-request-idzx-vercel-idÚviaÚserverzx-forwarded-forÚreturnúDict[str, Any]c                 ó6   — t          j         ¦   «         dddi ddœS )zëReturn a fresh per-attempt diagnostic dict.

    Mutated in-place by the streaming functions and read from the retry
    block when a stream dies.  Lives on ``request_client_holder`` so it
    survives across the closure boundary.
    Nr   )Ú
started_atÚfirst_chunk_atÚchunksÚbytesÚheadersÚhttp_status)Útime© ó    ú7/home/ragecks/.hermes/hermes-agent/agent/stream_diag.pyÚstream_diag_initr   )   s+   € õ ”i‘k”kØØØØØðð ð r   Úagentr   ÚdiagÚhttp_responseÚNonec                ó–  — |�t          |t          ¦  «        sdS 	 t          |dd¦  «        |d<   n# t          $ r Y nw xY w	 t          |dd¦  «        pi }i }t          | dt          ¦  «        }|D ]D}	 |                     |¦  «        }|rt          |¦  «        dd…         ||<   Œ5# t          $ r Y ŒAw xY w||d<   dS # t          $ r Y dS w xY w)u  Snapshot interesting headers + HTTP status from the live stream.

    Called once at stream open (before iterating chunks) so the metadata
    survives even if the stream dies before any chunk arrives.  Failures
    are swallowed â€” diag is best-effort.
    NÚstatus_coder   r   Ú_STREAM_DIAG_HEADERSéx   )Ú
isinstanceÚdictÚgetattrÚ	ExceptionÚSTREAM_DIAG_HEADERSÚgetÚstr)r   r   r   r   ÚcapturedÚtarget_headersÚnameÚvals           r   Ústream_diag_capture_responser+   :   s#  € ð Ð¥J¨tµTÑ$:Ô$:ÐØˆðÝ% m°]ÀDÑIÔIˆˆ]ÑÐøÝð ð ð ØˆðøøøðÝ˜-¨°DÑ9Ô9Ð?¸RˆØ#%ˆå  Ð(>Õ@SÑTÔTˆØ"ð 	ð 	ˆDðØ—k’k $Ñ'Ô'�Øð 4å%(¨¡X¤X¨d¨s¨d¤^�H˜T‘NøøÝð ð ð Ø�ðøøøà"ˆˆY‰ˆˆøÝð ð ð ØˆˆðøøøsB   ›0 °
=¼=Á/B: Á11B#Â"B: Â#
B0Â-B: Â/B0Â0B: Â:
CÃCÚerrorÚBaseExceptionr&   c                ó|  — g }| }|�mt          |¦  «        dk     rZ||v rnU|                     |¦  «         t          |dd¦  «        pt          |dd¦  «        }|�||u rn|}|�t          |¦  «        dk     °Zg }|D ]š}t          |¦  «                             ¦   «                              dd¦  «        }t          |¦  «        dk    r|dd…         dz   }|                     |rt          |¦  «        j        › d	|› d
�nt          |¦  «        j        ¦  «         Œ›|rd                     |¦  «        nt          | ¦  «        j        S )u¸  Return a compact ``Outer(msg) <- Inner(msg) <- ...`` rendering.

    OpenAI SDK wraps httpx errors as ``APIConnectionError`` /
    ``APIError`` and only the wrapper's class is visible at the catch
    site â€” but the underlying ``RemoteProtocolError`` /
    ``ConnectError`` / ``ReadError`` is what tells us WHY the stream
    died.  Walks ``__cause__`` then ``__context__`` (deduped, max 4
    deep) to surface the chain in one line.
    Né   Ú	__cause__Ú__context__Ú
Ú éŒ   õ   â€¦ú(ú)z <- )	ÚlenÚappendr"   r&   ÚstripÚreplaceÚtypeÚ__name__Újoin)r,   ÚseenÚlinkÚnxtÚpartsÚeÚmsgs          r   Úflatten_exception_chainrE   Y   sY  € ð !#€DØ$)€DØ
Ð
�s 4™yœy¨1š}˜}Ø�4ˆ<ˆ<ØØ�Š�DÑÔÐÝ�d˜K¨Ñ.Ô.ð 
µ'Ø�- ñ3
ô 3
ˆð ˆ;˜# ˜+˜+ØØˆð Ð
�s 4™yœy¨1š}˜}ð €EØð Pð PˆÝ�!‰fŒf�lŠl‰nŒn×$Ò$ T¨3Ñ/Ô/ˆÝˆs‰8Œ8�cŠ>ˆ>Ø�d�s�d”)˜eÑ#ˆCØ�Š°SÐN�˜Q™œÔ(Ð1Ð1¨3Ð1Ð1Ð1Ð1½dÀ1¹g¼gÔ>NÑOÔOÐOÐOØ!&Ð@ˆ6�;Š;�uÑÔÐ­D°©K¬KÔ,@Ð@r   )r   ÚkindÚattemptÚintÚmax_attemptsÚmid_tool_callÚboolúOptional[Dict[str, Any]]c               óŽ  — 	 	 |                       |¦  «        }n# t          $ r t          |¦  «        }Y nw xY w|r t          |¦  «        dk    r|dd…         dz   }	 t	          |¦  «        }n$# t          $ r t          |¦  «        j        }Y nw xY wt          j        ¦   «         }	d}
d}d}d}d}d}t          |t          ¦  «        �r^	 t          |                     d¦  «        pd¦  «        }
t          |                     d¦  «        pd¦  «        }t          |                     d	¦  «        p|	¦  «        }t          d|	|z
  ¦  «        }|                     d
¦  «        }|� t          dt          |¦  «        |z
  ¦  «        }|                     d¦  «        pi }t          |t          ¦  «        r3|r1d                     d„ |                     ¦   «         D ¦   «         ¦  «        }|                     d¦  «        �"t          |                     d¦  «        ¦  «        }n# t          $ r Y nw xY wt                                d|||t%          | dd¦  «        pdt%          | dd¦  «        | j        pd| j        pdt          |¦  «        j        ||||
|||�|d›d�nd|d|i¬¦  «         dS # t          $ r  t                                dd¬¦  «         Y dS w xY w)u  Record a transient stream-drop and retry to ``agent.log``.

    Always logs a structured WARNING so users have a breadcrumb regardless
    of UI verbosity.  Subagents in particular benefit because their
    retries no longer spam the parent's terminal â€” but the file log keeps
    full detail (provider, error class, attempt, base_url, subagent_id).

    When *diag* is provided (the per-attempt stream-diagnostic dict from
    :func:`stream_diag_init`), the WARNING also captures upstream headers
    (cf-ray, x-openrouter-provider, x-openrouter-id), HTTP status, bytes
    streamed before the drop, and elapsed time on the dying attempt.
    These are the breadcrumbs needed to answer "is one CF edge / one
    downstream provider responsible, or is it random across runs?"
    éð   Nr5   r   ç        Ú-r   r   r   r   r   r3   c              3  ó*   K  — | ]\  }}|› d |› �V — ŒdS )Ú=Nr   )Ú.0ÚkÚvs      r   ú	<genexpr>z#log_stream_retry.<locals>.<genexpr>±   sA   è è € ð -ð -Ù'+ q¨!˜1˜
˜
˜q˜
˜
ð-ð -ð -ð -ð -ð -r   r   u¾   Stream %s on attempt %s/%s â€” retrying. subagent_id=%s depth=%s provider=%s base_url=%s error_type=%s error=%s chain=%s http_status=%s bytes=%d chunks=%d elapsed=%.2fs ttfb=%s upstream=[%s]Ú_subagent_idÚ_delegate_depthz.2fÚsrJ   )Úextrazstream-retry log emit failedT)Úexc_info)Ú_summarize_api_errorr#   r&   r8   rE   r<   r=   r   r    r!   rH   r%   ÚfloatÚmaxr>   ÚitemsÚloggerÚwarningr"   ÚproviderÚbase_urlÚdebug)r   rF   r,   rG   rI   rJ   r   Ú_summaryÚ_chainÚ_nowÚ_bytesÚ_chunksÚ_elapsedÚ_ttfbÚ_headers_reprÚ_http_statusÚ_startedÚ_firstr   s                      r   Úlog_stream_retryrp   x   s:  € ð0CDð	"Ø×1Ò1°%Ñ8Ô8ˆHˆHøÝð 	"ð 	"ð 	"Ý˜5‘z”zˆHˆHˆHð	"øøøàð 	.�˜H™œ¨Ò+Ð+Ø   ”~¨Ñ-ˆHð	*Ý,¨UÑ3Ô3ˆFˆFøÝð 	*ð 	*ð 	*Ý˜%‘[”[Ô)ˆFˆFˆFð	*øøøõ Œy‰{Œ{ˆØˆØˆØˆØˆØˆØˆÝ�d�DÑ!Ô!ñ 	ðÝ˜TŸXšX gÑ.Ô.Ð3°!Ñ4Ô4�Ý˜dŸhšh xÑ0Ô0Ð5°AÑ6Ô6�Ý  §¢¨,Ñ!7Ô!7Ð!?¸4Ñ@Ô@�Ý˜s D¨8¡OÑ4Ô4�ØŸšÐ"2Ñ3Ô3�ØÐ%Ý ¥U¨6¡]¤]°XÑ%=Ñ>Ô>�EØŸ(š( 9Ñ-Ô-Ð3°�Ý˜g¥tÑ,Ô,ð °ð Ø$'§H¢Hð -ð -Ø/6¯}ª}©¬ð-ñ -ô -ñ %ô %�Mð —8’8˜MÑ*Ô*Ð6Ý#& t§x¢x°Ñ'>Ô'>Ñ#?Ô#?�LøøÝð ð ð Ø�ðøøøõ 	�Šðð ØØÝ�E˜>¨4Ñ0Ô0Ð7°CÝ�EÐ,¨aÑ0Ô0ØŒNÐ!˜cØŒNÐ!˜cÝ�‰KŒKÔ ØØØØØØØ$Ð0ˆuˆOˆOˆOˆOˆO°cØØ" MÐ2ð/ 	ñ 	
ô 	
ð 	
ð 	
ð 	
øõ2 ð Dð Dð DÝ�ŠÐ3¸dˆÑCÔCÐCÐCÐCÐCðDøøøsq   ƒ ˜J ™5²J ´5µ%J ÁA+ Á*J Á+BÂ	J ÂBÂ8J ÃEH ÈJ È
HÈJ ÈHÈA9J Ê&KËKc               ó2  — |rdnd}t          | ||||||¬¦  «         | j        pd}d}t          |t          ¦  «        r_	 |                     d¦  «        }	|	�6dt          d	t          j        ¦   «         t          |	¦  «        z
  ¦  «        d
›d�}n# t          $ r Y nw xY w	 |  	                    d|› d|› dt          |¦  «        j        › d|› d|› d|› �¦  «         |                      d|› d|› dt          |¦  «        j        › �¦  «         dS # t          $ r Y dS w xY w)u  Emit a single user-visible line for a stream drop+retry.

    Both top-level agents and subagents announce drops in the UI â€” the
    parent prefixes subagent lines with ``[subagent-N]`` via ``log_prefix``
    so they're easy to attribute.  All cases also write a structured
    WARNING to ``agent.log`` via :func:`log_stream_retry` with the full
    diagnostic detail (subagent_id, provider, base_url, error_type,
    cf-ray, x-openrouter-provider, bytes/chunks, elapsed) for post-hoc
    analysis.

    The user-visible status line is intentionally compact: provider,
    error class, attempt N/M, plus ``after Xs`` when the stream dropped
    mid-flight.  Full diagnostic detail goes to ``agent.log`` only â€”
    ``hermes logs --level WARNING | grep "Stream drop"`` to inspect.
    zdrop mid tool-callÚdrop)rF   r,   rG   rI   rJ   r   rb   Ú r   Nz after rO   z.1frY   u   âš ï¸� z stream z (r7   u    â€” reconnecting, retry ú/zstream retry )rp   rb   r    r!   r%   r^   r   r]   r#   Ú_buffer_statusr<   r=   Ú_touch_activity)
r   r,   rG   rI   rJ   r   rF   rb   Ú_suffixÚstarteds
             r   Úemit_stream_dropry   Ö   sÏ  € ð0 $1Ð<ÐÐ°f€DÝØØØØØ!Ø#Øðñ ô ð ð Œ~Ð+ €Hð €GÝ�$�ÑÔð ð	Ø—h’h˜|Ñ,Ô,ˆGØÐ"ØQ¥C¨­T¬Y©[¬[½5À¹>¼>Ñ-IÑ$JÔ$JÐQÐQÐQÐQ�øøÝð 	ð 	ð 	ØˆDð	øøøð
Ø×Òð@�hð @ð @¨ð @ð @µ°U±´Ô0Dð @ð @Àwð @ð @Ø'.ð@ð @Ø1=ð@ð @ñ	
ô 	
ð 	
ð 	×Òð,˜Gð ,ð , lð ,ð ,Ý˜%‘[”[Ô)ð,ð ,ñ	
ô 	
ð 	
ð 	
ð 	
øõ ð ð ð Øˆˆðøøøs%   ¾AB Â
BÂBÂA)D Ä
DÄD)r$   r   r+   rE   rp   ry   )r
   r   )r   r   r   r   r   r   r
   r   )r,   r-   r
   r&   )r   r   rF   r&   r,   r-   rG   rH   rI   rH   rJ   rK   r   rL   r
   r   )r   r   r,   r-   rG   rH   rI   rH   rJ   rK   r   rL   r
   r   )Ú__doc__Ú
__future__r   Úloggingr   Útypingr   r   r   r   Ú	getLoggerr=   r`   r$   r   r+   rE   rp   ry   Ú__all__r   r   r   ú<module>r€      s  ððð ð #Ð "Ð "Ð "Ð "Ð "à €€€Ø €€€Ø ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,à	ˆÔ	˜8Ñ	$Ô	$€ðÐ ðð ð ð ð"ð ð ð ð>Að Að Að AðN &*ð[Dð [Dð [Dð [Dð [Dð [DðJ &*ð8ð 8ð 8ð 8ð 8ð 8ðvð ð €€€r   