Ë
    áß²jÂ&  ã                  ó  — 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	„Zdd
œ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd„Zdd
œ	 	 	 	 	 	 	 	 	 	 	 	 	 dd„ZdZdd„Zdd„Zdd„Zdd„Zg d¢Zy)uÈ  Stream diagnostics â€” per-attempt counters, exception chains, retry logging.

When a streaming request dies mid-response these helpers record WHY (which CF
edge / OpenRouter downstream served it, bytes+chunks before the drop, HTTP
status, underlying httpx error class) to ``agent.log`` in full and to the
user-facing status line compactly. ``run_agent`` keeps thin forwarders so
existing call sites and tests patching ``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-forc                 ó8   — t        j                   «       dddi ddœS )zkFresh per-attempt diagnostic dict; mutated in place by the streaming functions and read by the retry block.Nr   )Ú
started_atÚfirst_chunk_atÚchunksÚbytesÚheadersÚhttp_status)Útime© ó    ú6/home/ubuntu/.hermes/hermes-agent/agent/stream_diag.pyÚstream_diag_initr      s   € äŸ)™)›+¸ÈÐUVÐceÐvzÑ{Ð{r   c                ó8  — |�t        |t        «      sy	 t        |dd«      |d<   	 t        |dd«      xs i }i }t        D ]'  }	 |j                  |«      x}rt        |«      dd ||<   Œ) ||d<   y# t        $ r Y ŒUw xY w# t        $ r Y ŒJw xY w# t        $ r Y yw xY w)zkSnapshot headers + HTTP status at stream open (so they survive a drop before the first chunk). Best-effort.NÚstatus_coder   r   éx   )Ú
isinstanceÚdictÚgetattrÚ	ExceptionÚSTREAM_DIAG_HEADERSÚgetÚstr)ÚagentÚdiagÚhttp_responser   ÚcapturedÚnameÚvals          r   Ústream_diag_capture_responser&      sÊ   € àÐ¤J¨t´TÔ$:ØðÜ% m°]ÀDÓIˆˆ]ÑðÜ˜-¨°DÓ9Ò?¸RˆØ#%ˆÜ'ò 	ˆDðØ!Ÿ+™+ dÓ+Ð+�3Ð+Ü%(¨£X¨d¨s ^�H˜T‘Nøð	ð #ˆˆYŠøô ò Ùðûô ò Ùðûô ò ÙðúsF   •A/ ¦B Á$A>Á'B Á/	A;Á:A;Á>	B
ÂB Â	B
Â
B Â	BÂBc                ó4  ‡— g }| }|�]t        |«      dk  rO||vrK|j                  |«       t        |dd«      xs t        |dd«      }|�||u rn|}|�t        |«      dk  r||vrŒKdd„Š|rdj                  ˆfd„|D «       «      S t	        | «      j
                  S )	a)  Compact ``Outer(msg) <- Inner(msg) <- ...`` rendering, walking ``__cause__`` then ``__context__``
    (deduped, max 4 deep): the OpenAI SDK wraps httpx errors so only the wrapper class is visible at
    the catch site; the inner RemoteProtocolError/ConnectError/ReadError says WHY the stream died.Né   Ú	__cause__Ú__context__c                óè   — t        | «      j                  «       j                  dd«      }t        |«      dkD  r|d d dz   n|}|rt	        | «      j
                  › d|› d�S t	        | «      j
                  S )Nú
ú éŒ   õ   â€¦ú(ú))r   ÚstripÚreplaceÚlenÚtypeÚ__name__)ÚeÚmsgs     r   Úrenderz'flatten_exception_chain.<locals>.renderA   si   € Ü�!‹f�l‰l‹n×$Ñ$ T¨3Ó/ˆÜ#& s£8¨c¢>ˆc�$�3ˆi˜%Ò°sˆÙ/2”$�q“'×"Ñ"Ð# 1 S E¨Ð+ÐH¼¸Q»×8HÑ8HÐHr   z <- c              3  ó.   •K  — | ]  } ‰|«      –— Œ y ­w©Nr   )Ú.0r7   r9   s     €r   ú	<genexpr>z*flatten_exception_chain.<locals>.<genexpr>F   s   øè ø€ Ò/ Q‘v˜a—yÑ/ùs   ƒ)r7   ÚBaseExceptionÚreturnr   )r4   Úappendr   Újoinr5   r6   )ÚerrorÚseenÚlinkÚnxtr9   s       @r   Úflatten_exception_chainrF   4   s¨   ø€ ð !#€DØ$)€DØ
Ð
œs 4›y¨1š}°¸TÑ1AØ�‰�DÔÜ�d˜K¨Ó.ÒT´'¸$ÀÈtÓ2TˆØˆ;˜# ™+ØØˆð Ð
œs 4›y¨1š}°¸TÒ1AóIñ
 48ˆ6�;‰;Ó/¨$Ô/Ó/ÐQ¼TÀ%»[×=QÑ=QÐQr   c                ó´  — dx}}d}dx}x}}t        | t        «      �r%	 t        j                  «       }t        | j	                  d«      xs d«      }t        | j	                  d«      xs d«      }t        | j	                  d«      xs |«      }t        d||z
  «      }| j	                  d«      }	|	�t        dt        |	«      |z
  «      d›d	�}| j	                  d
«      xs i }
t        |
t        «      r(|
r&dj                  d„ |
j                  «       D «       «      }| j	                  d«      �t        | j	                  d«      «      }||||||fS # t        $ r Y Œw xY w)za(http_status, bytes, chunks, elapsed, ttfb, upstream) for the retry log line; ``-`` when unknown.r   ç        ú-r   r   r   r   z.2fÚsr   r-   c              3  ó0   K  — | ]  \  }}|› d |› �–— Œ y­w)ú=Nr   )r<   ÚkÚvs      r   r=   z_diag_fields.<locals>.<genexpr>Z   s   è ø€ Ò(P¹¸¸1¨A¨3¨a°¨s¬Ñ(Pùs   ‚r   )r   r   r   Úintr   ÚfloatÚmaxrA   Úitemsr   r   )r!   Ú_bytesÚ_chunksÚ_elapsedÚ_ttfbÚ_headers_reprÚ_http_statusÚ_nowÚ_startedÚ_firstr   s              r   Ú_diag_fieldsr\   I   sJ  € àÐ€FˆWØ€HØ+.Ð.€EÐ.ˆM˜LÜ�$œÕð	Ü—9‘9“;ˆDÜ˜Ÿ™ 'Ó*Ò/¨aÓ0ˆFÜ˜$Ÿ(™( 8Ó,Ò1°Ó2ˆGÜ˜TŸX™X lÓ3Ò;°tÓ<ˆHÜ˜3  x¡Ó0ˆHØ—X‘XÐ.Ó/ˆFØÐ!Ü˜s¤E¨&£M°HÑ$<Ó=¸cÐBÀ!ÐD�Ø—h‘h˜yÓ)Ò/¨RˆGÜ˜'¤4Ô(©WØ #§¡Ñ(PÀÇÁÃÔ(PÓ P�Ø�x‰x˜Ó&Ð2Ü" 4§8¡8¨MÓ#:Ó;�ð ˜ ¨(°E¸=ÐHÐHøô ò 	Ùð	ús   ŸD$E Å	EÅE)r!   c               ó(  — 	 	 | j                  |«      }|rt        |«      dkD  r|dd dz   }	 t	        |«      }t        j                  d|||t        | dd«      xs dt        | dd«      | j                  xs d| j                  xs dt        |«      j                  ||gt        |«      ¢­d	d
|iiŽ y# t        $ r t        |«      }Y Œ²w xY w# t        $ r t        |«      j                  }Y Œ²w xY w# t        $ r t        j                  dd¬«       Y yw xY w)u9  Structured WARNING to ``agent.log`` for a transient stream drop + retry, always logged regardless of
    UI verbosity. With *diag*, also records upstream headers, HTTP status, bytes/chunks, elapsed and TTFB on
    the dying attempt â€” enough to tell "one CF edge / downstream provider" from "random across runs".éð   Nr/   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_idrI   Ú_delegate_depthr   ÚextraÚmid_tool_callzstream-retry log emit failedT)Úexc_info)Ú_summarize_api_errorr   r   r4   rF   r5   r6   ÚloggerÚwarningr   ÚproviderÚbase_urlr\   Údebug)	r    ÚkindrB   ÚattemptÚmax_attemptsrb   r!   Ú_summaryÚ_chains	            r   Úlog_stream_retryro   b   s(  € ðDð	"Ø×1Ñ1°%Ó8ˆHñ œ˜H›¨Ò+Ø  �~¨Ñ-ˆHð	*Ü,¨UÓ3ˆFô 	�‰ðtà�'˜<Ü�E˜>¨4Ó0Ò7°C¼ÀÐHYÐ[\Ó9]Ø�N‰NÒ!˜c 5§>¡>Ò#8°SÜ�‹K× Ñ  (¨Fð	
ô 6BÀ$Ó5Gò	
ð # MÐ2ó	
øô ò 	"Ü˜5“zŠHð	"ûô ò 	*Ü˜%“[×)Ñ)ŠFð	*ûô ò DÜ�‰Ð3¸dˆÖCðDúsQ   ƒB0 ”C. ­C
 ¸A7C. Â0CÃC. ÃCÃC. Ã
C+Ã(C. Ã*C+Ã+C. Ã. DÄDc               ó  — |rdnd}t        | ||||||¬«       | j                  xs d}d}	 t        |t        «      r|j	                  d«      nd}	|	�/dt        d	t        j                  «       t        |	«      z
  «      d
›d�}	 | j                  d|› d|› dt        |«      j                  › d|› d|› d|› �«       | j                  d|› d|› dt        |«      j                  › �«       y# t        $ r Y Œpw xY w# t        $ r Y yw xY w)zÔOne compact user-visible status line for a stream drop+retry, plus the full WARNING via log_stream_retry.
    ``after Xs`` distinguishes "couldn't connect" (0s) from "died mid-stream" (idle-kill / proxy timeout).zdrop mid tool-callÚdrop)rj   rB   rk   rl   rb   r!   rg   Ú r   Nú after rH   z.1frJ   u   âš ï¸� z stream z (r1   u    â€” reconnecting, retry ú/zstream retry )ro   rg   r   r   r   rQ   r   rP   r   Ú_buffer_diagnostic_statusr5   r6   Ú_touch_activity)
r    rB   rk   rl   rb   r!   rj   rg   Ú_suffixÚstarteds
             r   Úemit_stream_dropry   ‚   s+  € ñ $1Ñ°f€DÜØ�D ¨wÀ\ÐanÐuyõð �~‰~Ò+ €HØ€GðÜ,6°t¼TÔ,B�$—(‘(˜<Ô(ÈˆØÐØ¤ C¬¯©«´u¸W³~Ñ)EÓ FÀsÐKÈ1ÐMˆGðØ×'Ñ'Ø�h�Z˜x¨ v¨R´°U³×0DÑ0DÐ/EÀQÀwÀið P'Ø'. i¨q°°ð@ô	
ð 	×Ñ ¨g¨Y°a¸°~ÀWÌTÐRWË[×MaÑMaÐLbÐcÕdøô ò Ùðûô ò Ùðús%   ¬AC% ÂA#C4 Ã%	C1Ã0C1Ã4	D Ã?D i   c                ó®   — | }t        d«      D ]4  }|€ y	 t        |dd«      }|�|c S |j                  xs |j                  }Œ6 y# t        $ r d}Y Œ0w xY w)zfThe buffered ``httpx.Request`` carried by the exception chain (SDK wrapper or httpx error), else None.é   NÚrequest)Úranger   ÚRuntimeErrorr)   r*   )rB   rD   Ú_r|   s       r   Ú_failed_requestr€   ¤   sq   € à$)€DÜ�1‹Xò 	2ˆØˆ<Ùð	Ü˜d I¨tÓ4ˆGð ÐØŠNØ�~‰~Ò1 ×!1Ñ!1‰ð	2ð øô ò 	ØŠGð	ús   –AÁAÁAc                ó¾   — t        | dd «      }t        |t        «      rt        |j	                  d«      «      S t        |t
        t        t        f«      rt        |«      S y )NÚcontentzutf-8)r   r   r   r4   Úencoder   Ú	bytearrayÚ
memoryview)r|   r‚   s     r   Ú_request_body_bytesr†   ´   sL   € Ü�g˜y¨$Ó/€GÜ�'œ3ÔÜ�7—>‘> 'Ó*Ó+Ð+Ü�'œE¤9¬jÐ9Ô:Ü�7‹|ÐØr   c          	     ó4  — ddl m} t        | «      } |t        t	        |dd«      xs |xs d«      «      j
                  xs d}t        |«      }d|› d|› d	|d
k7  rdnd› �}|€|dz   S |dt        d
t        |dz  «      «      › d�z  }|t        k\  r|dz   S |dz   S )zúThe one user-facing line for "no stream event ever arrived and the connect retries are spent":
    names the host, the attempt count and the serialized request size so a body-size limit is
    distinguishable from an outage without reading agent.log.r   )ÚurlparseÚurlNrr   zthe endpointu   â�Œ Could not open a stream to rs   z attempté   rJ   u;   ; the endpoint looks unreachable â€” try again in a moment.z
 (request i   z KB)zH; the endpoint or a proxy in front of it may reject requests this large.)
Úurllib.parserˆ   r€   r   r   Úhostnamer†   rQ   ÚroundÚLARGE_REQUEST_HINT_BYTES)rB   Úattemptsrh   rˆ   r|   ÚhostÚsizeÚlines           r   Úconnect_exhausted_noticer“   ½   sÄ   € õ &Ü˜eÓ$€GÙ”Cœ ¨°Ó5ÒG¸ÒGÀRÓHÓI×RÑRÒdÐVd€DÜ˜wÓ'€DØ,¨T¨F°'¸(¸À8ÐS[Ð_`ÒS`ÉCÐfhÐKiÐj€DØ€|ØÐSÑSÐSØˆjœ˜Q¤ d¨T¡kÓ 2Ó3Ð4°DÐ9Ñ9€DØÔ'Ò'ØÐ`Ñ`Ð`ØÐOÑOÐOr   c               óŒ   ‡— t        |||¬«      Št        ˆfd„t        | dd«      xs dD «       «      ry| j                  ‰«       y)zÆBuffer :func:`connect_exhausted_notice` once per turn: the outer retry/fallback loop re-enters the
    stream call several times, and every pass exhausting the same budget must not add another copy.)r�   rh   c              3  ó@   •K  — | ]  \  }}t        |«      ‰k(  –— Œ y ­wr;   )r   )r<   Ú_kindr8   Útexts      €r   r=   z2buffer_connect_exhausted_notice.<locals>.<genexpr>Ò   s   øè ø€ Ò
a¡
  sŒ3ˆs‹8�tÕÑ
aùs   ƒÚ_retry_status_bufferNr   )r“   Úanyr   ru   )r    rB   r�   rh   r—   s       @r   Úbuffer_connect_exhausted_noticerš   Î   sC   ø€ ô $ E°HÀxÔP€DÜ
Ó
a¬g°eÐ=SÐUYÓ.ZÒ.`Ð^`Ô
aÔaØØ	×#Ñ# DÕ)r   )r   r“   rš   r   r&   rF   ro   ry   )r?   úDict[str, Any])r    r   r!   r›   r"   r   r?   ÚNone)rB   r>   r?   r   )r!   úOptional[Dict[str, Any]]r?   Útuple)r    r   rj   r   rB   r>   rk   rO   rl   rO   rb   Úboolr!   r�   r?   rœ   )r    r   rB   r>   rk   rO   rl   rO   rb   rŸ   r!   r�   r?   rœ   )rB   r>   r?   r   )r|   r   r?   zOptional[int])rB   r>   r�   rO   rh   r   r?   r   )
r    r   rB   r>   r�   rO   rh   r   r?   rœ   )Ú__doc__Ú
__future__r   Úloggingr   Útypingr   r   r   r   Ú	getLoggerr6   re   r   r   r&   rF   r\   ro   ry   rŽ   r€   r†   r“   rš   Ú__all__r   r   r   ú<module>r¦      s
  ðñõ #ã Û ß ,Ó ,à	ˆ×	Ñ	˜8Ó	$€ðÐ ó|ó
ó,Ró*Ið6 ;?ñDØðDØðDØ%2ðDØ=@ðDØPSðDàðDà7ðDð 
óDðD ;?ñØðØ'ðØ25ðØEHðàðà7ðð 
óð> &Ð óó óPó"*ò	�r   