§
    ‰eîi;¡  ã                  óú   — d Z ddlmZ ddlZddlZddlZddlZddlZddlm	Z	 ddl
mZmZ  ej        d¦  «        Z e¦   «         Z e¦   «         Z e¦   «         Ze	 G d„ d¦  «        ¦   «         Z G d	„ d
¦  «        ZdS )u~  Gateway streaming consumer â€” bridges sync agent callbacks to async platform delivery.

The agent fires stream_delta_callback(text) synchronously from its worker thread.
GatewayStreamConsumer:
  1. Receives deltas via on_delta() (thread-safe, sync)
  2. Queues them to an asyncio task via queue.Queue
  3. The async run() task buffers, rate-limits, and progressively edits
     a single message on the target platform

Design: Uses the edit transport (send initial message, then editMessageText).
This is universally supported across Telegram, Discord, and Slack.

Credit: jobless0x (#774, #1312), OutThisLife (#798), clicksingh (#697).
é    )ÚannotationsN)Ú	dataclass)ÚAnyÚOptionalzgateway.stream_consumerc                  óL   — e Zd ZU dZdZded<   dZded<   dZd	ed
<   dZded<   dS )ÚStreamConsumerConfigz5Runtime config for a single stream consumer instance.g      ð?ÚfloatÚedit_intervalé(   ÚintÚbuffer_thresholdu    â–‰ÚstrÚcursorFÚboolÚbuffer_onlyN)	Ú__name__Ú
__module__Ú__qualname__Ú__doc__r
   Ú__annotations__r   r   r   © ó    ú9/home/div/.hermes/hermes-agent/gateway/stream_consumer.pyr   r   (   s]   € € € € € € à?Ð?Ø€MÐÐÐÑØÐÐÐÐÑØ€FÐÐÐÑØ€KÐÐÐÑÐÐr   r   c                  ó@  — e Zd ZdZdZdZdZ	 	 d4d5d„Zed6d„¦   «         Z	ed6d„¦   «         Z
d7d„Zd8d„Zddœd9d„Zd8d„Zd7d„Zd8d„Zd7d„Zd7d„Z ej        d ¦  «        Zed:d!„¦   «         Zd;d$„Zd<d%„Zd=d'„Zed>d+„¦   «         Zd8d,„Zd6d-„Zd7d.„Zd7d/„Zd?d0„Z dd1œd@d3„Z!dS )AÚGatewayStreamConsumera2  Async consumer that progressively edits a platform message with streamed tokens.

    Usage::

        consumer = GatewayStreamConsumer(adapter, chat_id, config, metadata=metadata)
        # Pass consumer.on_delta as stream_delta_callback to AIAgent
        agent = AIAgent(..., stream_delta_callback=consumer.on_delta)
        # Start the consumer as an asyncio task
        task = asyncio.create_task(consumer.run())
        # ... run agent in thread pool ...
        consumer.finish()  # signal completion
        await task         # wait for final edit
    é   )z<REASONING_SCRATCHPAD>z<think>z<reasoning>z
<THINKING>z
<thinking>z	<thought>)z</REASONING_SCRATCHPAD>z</think>z</reasoning>z</THINKING>z</thinking>z
</thought>NÚadapterr   Úchat_idr   ÚconfigúOptional[StreamConsumerConfig]ÚmetadataúOptional[dict]c                ó„  — || _         || _        |pt          ¦   «         | _        || _        t          j        ¦   «         | _        d| _        d | _	        d| _
        d| _        d| _        d| _        d| _        d| _        d| _        | j        j        | _        d| _        t)          |dd¦  «        du | _        d| _        d| _        d S )NÚ FTg        r   ÚREQUIRES_EDIT_FINALIZE)r   r   r   Úcfgr!   ÚqueueÚQueueÚ_queueÚ_accumulatedÚ_message_idÚ_already_sentÚ_edit_supportedÚ_last_edit_timeÚ_last_sent_textÚ_fallback_final_sendÚ_fallback_prefixÚ_flood_strikesr
   Ú_current_edit_intervalÚ_final_response_sentÚgetattrÚ_adapter_requires_finalizeÚ_in_think_blockÚ_think_buffer)Úselfr   r   r   r!   s        r   Ú__init__zGatewayStreamConsumer.__init__P   sÍ   € ð ˆŒØˆŒØÐ3Õ1Ñ3Ô3ˆŒØ ˆŒÝ#(¤;¡=¤=ˆŒØˆÔØ*.ˆÔØ"ˆÔØ#ˆÔØ"ˆÔØ!ˆÔØ$)ˆÔ!Ø "ˆÔØˆÔØ&*¤hÔ&<ˆÔ#Ø$)ˆÔ!õ �GÐ5°uÑ=Ô=ÀÐEð 	Ô'ð
  %ˆÔØˆÔÐÐr   Úreturnr   c                ó   — | j         S )z?True if at least one message was sent or edited during the run.)r,   ©r9   s    r   Úalready_sentz"GatewayStreamConsumer.already_sentt   s   € ð Ô!Ð!r   c                ó   — | j         S )zBTrue when the stream consumer delivered the final assistant reply.)r4   r=   s    r   Úfinal_response_sentz)GatewayStreamConsumer.final_response_senty   s   € ð Ô(Ð(r   ÚNonec                óD   — | j                              t          ¦  «         dS )z>Finalize the current stream segment and start a fresh message.N)r)   ÚputÚ_NEW_SEGMENTr=   s    r   Úon_segment_breakz&GatewayStreamConsumer.on_segment_break~   s   € àŒ�Š�Ñ%Ô%Ð%Ð%Ð%r   Útextc                óP   — |r#| j                              t          |f¦  «         dS dS )z7Queue a completed interim assistant commentary message.N)r)   rC   Ú_COMMENTARY©r9   rF   s     r   Úon_commentaryz#GatewayStreamConsumer.on_commentary‚   s3   € àð 	1ØŒK�OŠO�[¨$Ð/Ñ0Ô0Ð0Ð0Ð0ð	1ð 	1r   F©Úpreserve_no_editrL   c               ój   — |r| j         dk    rd S d | _         d| _        d| _        d| _        d| _        d S )NÚ__no_edit__r$   F)r+   r*   r/   r0   r1   )r9   rL   s     r   Ú_reset_segment_statez*GatewayStreamConsumer._reset_segment_state‡   sJ   € Øð 	 Ô 0°MÒ AÐ AØˆFØˆÔØˆÔØ!ˆÔØ$)ˆÔ!Ø "ˆÔÐÐr   c                ór   — |r| j                              |¦  «         dS |€|                      ¦   «          dS dS )u2  Thread-safe callback â€” called from the agent's worker thread.

        When *text* is ``None``, signals a tool boundary: the current message
        is finalized and subsequent text will be sent as a new message so it
        appears below any tool-progress messages the gateway sent in between.
        N)r)   rC   rE   rI   s     r   Úon_deltazGatewayStreamConsumer.on_delta�   sJ   € ð ð 	$ØŒK�OŠO˜DÑ!Ô!Ð!Ð!Ð!Øˆ\Ø×!Ò!Ñ#Ô#Ð#Ð#Ð#ð ˆ\r   c                óD   — | j                              t          ¦  «         dS )z#Signal that the stream is complete.N)r)   rC   Ú_DONEr=   s    r   ÚfinishzGatewayStreamConsumer.finishœ   s   € àŒ�Š�ÑÔÐÐÐr   c                ó  — | j         |z   }d| _         |�rm| j        r£d}d}| j        D ]:}|                     |¦  «        }|dk    r|dk    s||k     r|}t	          |¦  «        }Œ;|rd| _        |||z   d…         }�nt          d„ | j        D ¦   «         ¦  «        }t	          |¦  «        |k    r|| d…         n|| _         dS d}d}| j        D ]ô}d}	 |                     ||¦  «        }|dk    rnÒ|dk    r#| j         p| j                             d¦  «        }	nƒ|d|…         }
|
 	                    d¦  «        }|dk    r;| j         s| j                             d¦  «        o|
 
                    ¦   «         dk    }	n#|
|d	z   d…          
                    ¦   «         dk    }	|	r|dk    s||k     r|}t	          |¦  «        }n|d	z   }ŒïŒõ|r-| xj        |d|…         z  c_        d| _        |||z   d…         }n�d}| j        D ]G}t          d	t	          |¦  «        ¦  «        D ]'}|                     |d|…         ¦  «        r||k    r|}Œ(ŒH|r*| xj        |d| …         z  c_        || d…         | _         n| xj        |z  c_        dS |�°kdS dS )
ab  Add a text delta to the accumulated buffer, suppressing think blocks.

        Uses a state machine that tracks whether we are inside a
        reasoning/thinking block.  Text inside such blocks is silently
        discarded.  Partial tags at buffer boundaries are held back in
        ``_think_buffer`` until enough characters arrive to decide.
        r$   éÿÿÿÿr   FNc              3  ó4   K  — | ]}t          |¦  «        V — Œd S )N)Úlen)Ú.0Úts     r   ú	<genexpr>z?GatewayStreamConsumer._filter_and_accumulate.<locals>.<genexpr>Å   s(   è è € Ð!IÐ!I¨Q¥# a¡&¤&Ð!IÐ!IÐ!IÐ!IÐ!IÐ!Ir   TÚ
é   )r8   r7   Ú_CLOSE_THINK_TAGSÚfindrX   ÚmaxÚ_OPEN_THINK_TAGSr*   ÚendswithÚrfindÚstripÚrange)r9   rF   ÚbufÚbest_idxÚbest_lenÚtagÚidxÚmax_tagÚsearch_startÚis_boundaryÚ	precedingÚlast_nlÚ	held_backÚis                 r   Ú_filter_and_accumulatez,GatewayStreamConsumer._filter_and_accumulate¨   sd  € ð Ô  4Ñ'ˆØˆÔàñ K	ØÔ#ð Jà�Ø�ØÔ1ð ,ð ,�CØŸ(š( 3™-œ-�CØ˜b’y�y h°"¢n n¸¸hº¸Ø#&˜Ý#& s¡8¤8˜øàð 	à+0�DÔ(Ø˜h¨Ñ1Ð2Ð2Ô3�C‘Cõ "Ð!IÐ!I°$Ô2HÐ!IÑ!IÔ!IÑIÔI�GÝ;>¸s¹8¼8ÀgÒ;MÐ;M¨¨g¨X¨Y¨Y¬¨ÐSV�DÔ&Ø�Fð �Ø�ØÔ0ð /ð /�CØ#$�Lð/Ø!Ÿhšh s¨LÑ9Ô9˜Ø "š9˜9Ø!à !š8˜8à$(Ô$5Ð 5ð !DØ#'Ô#4×#=Ò#=¸dÑ#CÔ#Cð (˜Kð
 ),¨D¨S¨D¬	˜IØ&/§o¢o°dÑ&;Ô&;˜GØ&¨"š}˜}à)-Ô):Ð%:ð &IØ(,Ô(9×(BÒ(BÀ4Ñ(HÔ(Hð%@à(1¯ªÑ(9Ô(9¸RÒ(?ð !, ð /8¸À!¹¸¸Ô.E×.KÒ.KÑ.MÔ.MÐQSÒ.S à&ð "¨H¸ªN¨N¸cÀHºn¸nØ'*˜HÝ'*¨3¡x¤x˜HØ!Ø'*¨Q¡w˜ð5/øð8 ð àÐ%Ô%¨¨Y¨h¨Y¬Ñ7Ð%Ô%Ø+/�DÔ(Ø˜h¨Ñ1Ð2Ð2Ô3�C�Cð !"�IØ#Ô4ð .ð .˜Ý!& q­#¨c©(¬(Ñ!3Ô!3ð .ð .˜AØ"Ÿ|š|¨C°°°¬GÑ4Ô4ð .¸¸Yº¸Ø,- 	øð.ð !ð 1ØÐ)Ô)¨S°°9°*°Ô-=Ñ=Ð)Ô)Ø-0°)°°°Ô-=˜Ô*Ð*àÐ)Ô)¨SÑ0Ð)Ô)Ø�FðW ñ K	ð K	ð K	ð K	ð K	r   c                ób   — | j         r%| j        s | xj        | j         z  c_        d| _         dS dS dS )zÖFlush any held-back partial-tag buffer into accumulated text.

        Called when the stream ends (got_done) so that partial text that
        was held back waiting for a possible opening tag is not lost.
        r$   N)r8   r7   r*   r=   s    r   Ú_flush_think_bufferz)GatewayStreamConsumer._flush_think_buffer   sR   € ð Ôð 	$ dÔ&:ð 	$ØÐÔ Ô!3Ñ3ÐÔØ!#ˆDÔÐÐð	$ð 	$ð 	$ð 	$r   c              ƒ  óÌ
  K  — t          | j        dd¦  «        }t          d|t          | j        j        ¦  «        z
  dz
  ¦  «        }	 	 d}d}d}	 	 | j                             ¦   «         }|t          u rd}nx|t          u rd}nlt          |t          ¦  «        r+t          |¦  «        dk    r|d	         t          u r	|d
         }n,|                      |¦  «         n# t          j        $ r Y nw xY wŒž|r|                      ¦   «          t#          j        ¦   «         }|| j        z
  }|p|p|du}	| j        j        s6|	p3|| j        k    r| j        p!t          | j        ¦  «        | j        j        k    }	d}
|	�ré| j        �rát          | j        ¦  «        |k    rœ| j        €•| j                             | j        |¦  «        }|D ]#}|                      || j        ¦  «        ƒ d{V —† Œ$d| _        d| _        t#          j        ¦   «         | _        |r| j        | _        dS |rd| _        d| _        d| _        �Œät          | j        ¦  «        |k    rÄ| j        �½| j         r¶| j         !                    dd	|¦  «        }||dz  k     r|}| j        d|…         }|  "                    |¦  «        ƒ d{V —†}| j        s|sn[| j        |d…          #                    d¦  «        | _        d| _        d| _        t          | j        ¦  «        |k    r| j        �| j         °¶| j        }|s|s|€|| j        j        z  }|  "                    ||¬¦  «        ƒ d{V —†}
t#          j        ¦   «         | _        |r�| j        r”| j        r!|  $                    | j        ¦  «        ƒ d{V —† nl|
r| j%        sd| _        n[| j        r(|  "                    | j        d¬¦  «        ƒ d{V —†| _        n,| j        s%|  "                    | j        ¦  «        ƒ d{V —†| _        dS |�[|  &                    ¦   «          |  '                    |¦  «        ƒ d{V —† t#          j        ¦   «         | _        |  &                    ¦   «          |rK| j        r.|
s,| j        r%| j        dk    r|  (                    ¦   «         ƒ d{V —† |  &                    d¬¦  «         tS          j*        d¦  «        ƒ d{V —† �Œu# tR          j+        $ ri d}| j        rF| j        r?	 tY          |  "                    | j        ¦  «        ƒ d{V —†¦  «        }n# tZ          $ r Y nw xY w|r| j        sd| _        Y dS Y dS Y dS tZ          $ r&}t\           /                    d|¦  «         Y d}~dS d}~ww xY w)z@Async task that drains the queue and edits the platform message.ÚMAX_MESSAGE_LENGTHé   éô  éd   TFNé   r   r]   r$   r\   ©ÚfinalizerN   rK   gš™™™™™©?zStream consumer error: %s)0r5   r   r`   rX   r&   r   r)   Ú
get_nowaitrS   rD   Ú
isinstanceÚtuplerH   rr   r'   ÚEmptyrt   ÚtimeÚ	monotonicr.   r   r3   r*   r   r+   Útruncate_messageÚ_send_new_chunkr/   r,   r4   r0   r1   r-   rc   Ú_send_or_editÚlstripÚ_send_fallback_finalr6   rO   Ú_send_commentaryÚ#_flush_segment_tail_on_edit_failureÚasyncioÚsleepÚCancelledErrorr   Ú	ExceptionÚloggerÚerror)r9   Ú
_raw_limitÚ_safe_limitÚgot_doneÚgot_segment_breakÚcommentary_textÚitemÚnowÚelapsedÚshould_editÚcurrent_update_visibleÚchunksÚchunkÚsplit_atÚokÚdisplay_textÚ_best_effort_okÚes                     r   ÚrunzGatewayStreamConsumer.run
  s\  è è € õ ˜Tœ\Ð+?ÀÑFÔFˆ
Ý˜#˜z­C°´´Ñ,@Ô,@Ñ@À3ÑFÑGÔGˆðF	9ðq*à �Ø$)Ð!Ø"&�ððØ#œ{×5Ò5Ñ7Ô7˜Ø¥5˜=˜=Ø'+˜HØ!Ø¥<Ð/Ð/Ø04Ð-Ø!Ý% d­EÑ2Ô2ð "µs¸4±y´yÀA²~°~È$ÈqÌ'ÕU`ÐJ`ÐJ`Ø.2°1¬g˜OØ!Ø×3Ò3°DÑ9Ô9Ð9Ð9øÝ œ;ð ð ð Ø˜ðøøøðð& ð /Ø×,Ò,Ñ.Ô.Ð.õ ”nÑ&Ô&�Ø Ô 4Ñ4�àð 3Ø(ð3à&¨dÐ2ð ð
 ”xÔ+ð Ø"-ð #Ø  DÔ$?Ò?ð 2Ø $Ô 1ðOå˜tÔ0Ñ1Ô1°T´XÔ5NÒNð  ð */Ð&Øñ B< 4Ô#4ñ B<õ ˜DÔ-Ñ.Ô.°Ò<Ð<Ø Ô,Ð4ð "&¤×!>Ò!>Ø Ô-¨{ñ"ô "˜ð &,ð Pð P˜EØ"&×"6Ò"6°u¸dÔ>NÑ"OÔ"OÐOÐOÐOÐOÐOÐOÐOÐOØ,.˜Ô)Ø/1˜Ô,Ý/3¬~Ñ/?Ô/?˜Ô,Ø#ð #Ø8<Ô8J˜DÔ5Ø"˜FØ,ð 7Ø/3˜DÔ,Ø8=˜DÔ5Ø46˜DÔ1Ù õ
 ˜DÔ-Ñ.Ô.°Ò<Ð<Ø Ô,Ð8Ø Ô0ð 9ð $(Ô#4×#:Ò#:¸4ÀÀKÑ#PÔ#P˜Ø# k°QÑ&6Ò6Ð6Ø'2˜HØ $Ô 1°)°8°)Ô <˜Ø#'×#5Ò#5°eÑ#<Ô#<Ð<Ð<Ð<Ð<Ð<Ð<˜ØÔ4ð "¸Bð "ð "Ø,0Ô,=¸h¸i¸iÔ,H×,OÒ,OÐPTÑ,UÔ,U˜Ô)Ø+/˜Ô(Ø/1˜Ô,õ% ˜DÔ-Ñ.Ô.°Ò<Ð<Ø Ô,Ð8Ø Ô0ð 9ð& $(Ô#4�LØ#ð 8Ð,=ð 8À/ÐBYØ$¨¬¬Ñ7˜ð 48×3EÒ3EØ$Ø!2ð 4Fñ 4ô 4ð .ð .ð .ð .ð .ð .Ð*õ ,0¬>Ñ+;Ô+;�DÔ(àð ð
 Ô(ð dØÔ4ð dØ"&×";Ò";¸DÔ<MÑ"NÔ"NÐNÐNÐNÐNÐNÐNÐNÐNà2ðdà$(Ô$Cðdð 9=˜DÔ5Ð5Ø!Ô-ð dð ?C×>PÒ>PØ $Ô 1¸Dð ?Qñ ?ô ?ð 9ð 9ð 9ð 9ð 9ð 9˜DÔ5Ð5ð "&Ô!3ð dØ>B×>PÒ>PÐQUÔQbÑ>cÔ>cÐ8cÐ8cÐ8cÐ8cÐ8cÐ8c˜DÔ5Ø�Fà"Ð.Ø×-Ò-Ñ/Ô/Ð/Ø×/Ò/°Ñ@Ô@Ð@Ð@Ð@Ð@Ð@Ð@Ð@Ý+/¬>Ñ+;Ô+;�DÔ(Ø×-Ò-Ñ/Ô/Ð/ð  %ð Eð Ô)ðIà 6ðIð !Ô,ðIð !Ô,°Ò=Ð=à"×FÒFÑHÔHÐHÐHÐHÐHÐHÐHÐHØ×-Ò-¸tÐ-ÑDÔDÐDå”m DÑ)Ô)Ð)Ð)Ð)Ð)Ð)Ð)Ð)ñcq*øõf Ô%ð 	1ð 	1ð 	1à#ˆOØÔ ð  TÔ%5ð ðÝ&*°×1CÒ1CÀDÔDUÑ1VÔ1VÐ+VÐ+VÐ+VÐ+VÐ+VÐ+VÑ&WÔ&W�O�OøÝ ð ð ð Ø�Dðøøøð ð 1 tÔ'@ð 1Ø,0�Ô)Ð)Ð)Ð)ð1ð 1ð 1ð 1ð 1ð 1åð 	9ð 	9ð 	9Ý�LŠLÐ4°aÑ8Ô8Ð8Ð8Ð8Ð8Ð8Ð8Ð8øøøøð	9øøøs™   ÁR= Á$C Á4R= Á5C Â R= Â?C Ã R= ÃC ÃR= ÃC)Ã&R= Ã(C)Ã)D'R= ÈG#R= Ï7CR= Ò=U#Ó-TÔ
U#Ô
TÔU#ÔTÔU#Ô4	U#Ô=UÕU#z[`"']?MEDIA:\s*\S+[`"']?c                óÖ   — d| vrd| vr| S |                       dd¦  «        }t          j                             d|¦  «        }t	          j        dd|¦  «        }|                     ¦   «         S )uØ  Strip MEDIA: directives and internal markers from text before display.

        The streaming path delivers raw text chunks that may include
        ``MEDIA:<path>`` tags and ``[[audio_as_voice]]`` directives meant for
        the platform adapter's post-processing.  The actual media files are
        delivered separately via ``_deliver_media_from_response()`` after the
        stream finishes â€” we just need to hide the raw directives from the
        user.
        zMEDIA:z[[audio_as_voice]]r$   z\n{3,}z

)Úreplacer   Ú	_MEDIA_REÚsubÚreÚrstrip)rF   Úcleaneds     r   Ú_clean_for_displayz(GatewayStreamConsumer._clean_for_displayÝ  sn   € ð ˜4ÐÐÐ$8ÀÐ$DÐ$DØˆKØ—,’,Ð3°RÑ8Ô8ˆÝ'Ô1×5Ò5°b¸'ÑBÔBˆå”&˜ F¨GÑ4Ô4ˆà�~Š~ÑÔÐr   Úreply_to_idúOptional[str]c              ƒ  óö  K  — |                       |¦  «        }|                     ¦   «         s|S 	 | j        rt          | j        ¦  «        ni }| j                             | j        |||¬¦  «        ƒ d{V —†}|j        rB|j        r;t          |j        ¦  «        | _
        d| _        || _        t          |j        ¦  «        S d| _        |S # t          $ r'}t                               d|¦  «         |cY d}~S d}~ww xY w)z–Send a new message chunk, optionally threaded to a previous message.

        Returns the message_id so callers can thread subsequent chunks.
        )r   ÚcontentÚreply_tor!   NTFzStream send chunk error: %s)r©   rd   r!   Údictr   Úsendr   ÚsuccessÚ
message_idr   r+   r,   r/   r-   r�   rŽ   r�   )r9   rF   rª   ÚmetaÚresultr    s         r   r„   z%GatewayStreamConsumer._send_new_chunkñ  s:  è è € ð
 ×&Ò& tÑ,Ô,ˆØ�zŠz‰|Œ|ð 	ØÐð	Ø*.¬-Ð?•4˜œÑ&Ô&Ð&¸RˆDØœ<×,Ò,ØœØØ$Øð	 -ñ ô ð ð ð ð ð ð ˆFð Œ~ð # &Ô"3ð #Ý#& vÔ'8Ñ#9Ô#9�Ô Ø%)�Ô"Ø'+�Ô$Ý˜6Ô,Ñ-Ô-Ð-à',�Ô$Ø"Ð"øÝð 	ð 	ð 	Ý�LŠLÐ6¸Ñ:Ô:Ð:ØÐÐÐÐÐÐøøøøð	øøøs$   ¯BC Â>C Ã
C8ÃC3Ã-C8Ã3C8c                óØ   — | j         pd}| j        j        rA|                     | j        j        ¦  «        r"|dt	          | j        j        ¦  «         …         }|                      |¦  «        S )z>Return the visible text already shown in the streamed message.r$   N)r/   r&   r   rb   rX   r©   ©r9   Úprefixs     r   Ú_visible_prefixz%GatewayStreamConsumer._visible_prefix  se   € àÔ%Ð+¨ˆØŒ8Œ?ð 	4˜vŸš¨t¬x¬Ñ?Ô?ð 	4ØÐ2�c $¤(¤/Ñ2Ô2Ð2Ð2Ô3ˆFØ×&Ò& vÑ.Ô.Ð.r   Ú
final_textc                ó¼   — | j         p|                      ¦   «         }|r>|                     |¦  «        r)|t          |¦  «        d…                              ¦   «         S |S )zAReturn only the part of final_text the user has not already seen.N)r1   r¸   Ú
startswithrX   r†   )r9   r¹   r·   s      r   Ú_continuation_textz(GatewayStreamConsumer._continuation_text  s_   € àÔ&Ð@¨$×*>Ò*>Ñ*@Ô*@ˆØð 	5�j×+Ò+¨FÑ3Ô3ð 	5Ø�c &™kœk˜l˜lÔ+×2Ò2Ñ4Ô4Ð4ØÐr   Úlimitr   ú	list[str]c                ól  — t          | ¦  «        |k    r| gS g }| }t          |¦  «        |k    ro|                     dd|¦  «        }||dz  k     r|}|                     |d|…         ¦  «         ||d…                              d¦  «        }t          |¦  «        |k    °o|r|                     |¦  «         |S )z;Split text into reasonably sized chunks for fallback sends.r\   r   rz   N)rX   rc   Úappendr†   )rF   r½   rš   Ú	remainingrœ   s        r   Ú_split_text_chunksz(GatewayStreamConsumer._split_text_chunks  sË   € õ ˆt‰9Œ9˜ÒÐØ�6ˆMØˆØˆ	Ý�)‰nŒn˜uÒ$Ð$Ø —’ t¨Q°Ñ6Ô6ˆHØ˜% 1™*Ò$Ð$Ø �Ø�MŠM˜) I X IÔ.Ñ/Ô/Ð/Ø! ( ) )Ô,×3Ò3°DÑ9Ô9ˆIõ �)‰nŒn˜uÒ$Ð$ð ð 	%Ø�MŠM˜)Ñ$Ô$Ð$Øˆr   c              ƒ  óJ  K  — |                       |¦  «        }|                      |¦  «        }d| _        |                     ¦   «         sñ|                     ¦   «         r||                      ¦   «         k    r|}nÂ| j        r«| j        r¤| j        j        r˜| j         	                    | j        j        ¦  «        rt| j        dt          | j        j        ¦  «         …         }	 | j                             | j        | j        |¬¦  «        ƒ d{V —†}|j        r|| _        n# t          $ r Y nw xY wd| _        d| _        dS t%          | j        dd¦  «        }t'          d|dz
  ¦  «        }|                      ||¦  «        }d}	d	}
d}|D ]ù}d}t+          d
¦  «        D ]ˆ}| j                             | j        || j        ¬¦  «        ƒ d{V —†}|j        r nQ|dk    rJ|                      |¦  «        r5t2                               d¦  «         t7          j        d¦  «        ƒ d{V —† Œˆ |r|j        sG|r&d| _        d| _        |	| _        |
| _        d	| _         dS d| _        d| _        d	| _        d	| _         dS d}|}
|j        p|	}	Œú|	| _        d| _        d| _        |d         | _        d	| _        dS )z—Send the final continuation after streaming edits stop working.

        Retries each chunk once on flood-control failures with a short delay.
        FN©r   r²   r­   Trv   rw   rx   ry   r$   rz   ©r   r­   r!   r   z.Flood control on fallback send, retrying in 3sg      @rV   )r©   r¼   r0   rd   r¸   r+   r/   r&   r   rb   rX   r   Úedit_messager   r±   r�   r,   r4   r5   r`   rÂ   re   r°   r!   Ú_is_flood_errorrŽ   ÚdebugrŠ   r‹   r1   r²   )r9   rF   r¹   ÚcontinuationÚ
clean_textr´   Ú	raw_limitÚ
safe_limitrš   Úlast_message_idÚlast_successful_chunkÚsent_any_chunkr›   Úattempts                 r   r‡   z*GatewayStreamConsumer._send_fallback_final,  sj  è è € ð
 ×,Ò,¨TÑ2Ô2ˆ
Ø×.Ò.¨zÑ:Ô:ˆØ$)ˆÔ!Ø×!Ò!Ñ#Ô#ð $	ð ×ÒÑ!Ô!ð  j°D×4HÒ4HÑ4JÔ4JÒ&JÐ&JØ)��ð Ô$ðàÔ,ðð œœðð Ô,×5Ò5°d´h´oÑFÔFð	ð "&Ô!5Ð6L½¸D¼H¼OÑ8LÔ8LÐ7LÐ6LÔ!M�Jð	Ø'+¤|×'@Ò'@Ø$(¤LØ'+Ô'7Ø$.ð (Añ (ô (ð "ð "ð "ð "ð "ð "˜ð
 "œ>ð >Ø3=˜DÔ0øøÝ$ð ð ð Ø˜ðøøøà%)�Ô"Ø,0�Ô)Ø�å˜DœLÐ*>ÀÑEÔEˆ	Ý˜˜i¨#™oÑ.Ô.ˆ
Ø×(Ò(¨°zÑBÔBˆà)-ˆØ "ÐØˆØð '	Cð '	CˆEàˆFÝ  ™8œ8ð ð �Ø#œ|×0Ò0Ø œLØ!Ø!œ]ð  1ñ  ô  ð ð ð ð ð ð �ð
 ”>ð Ø�EØ˜a’<�< D×$8Ò$8¸Ñ$@Ô$@�<Ý—L’LØHñô ð õ "œ-¨Ñ,Ô,Ð,Ð,Ð,Ð,Ð,Ð,Ð,Ð,ààð  ¤ð Ø!ð 	ð *.�DÔ&Ø04�DÔ-Ø'6�DÔ$Ø+@�DÔ(Ø,.�DÔ)Ø�F�Fð &+�Ô"Ø#'�Ô Ø')�Ô$Ø(*�Ô%Ø��Ø!ˆNØ$)Ð!Ø$Ô/ÐB°?ˆOˆOà*ˆÔØ!ˆÔØ$(ˆÔ!Ø% bœzˆÔØ "ˆÔÐÐs   Ã;D Ä
D&Ä%D&c                óh   — t          |dd¦  «        pd}|                     ¦   «         }d|v pd|v pd|v S )zFCheck if a SendResult failure is due to flood control / rate limiting.r�   r$   Úfloodzretry afterÚrate)r5   Úlower)r9   r´   ÚerrÚ	err_lowers       r   rÇ   z%GatewayStreamConsumer._is_flood_error�  sD   € å�f˜g rÑ*Ô*Ð0¨bˆØ—I’I‘K”Kˆ	Ø˜)Ð#ÐX }¸	Ð'AÐXÀVÈyÐEXÐXr   c              ƒ  óH  K  — | j         s|                      ¦   «         ƒ d{V —† | j        p|                      ¦   «         }| j        }|r>|                     |¦  «        r)|t          |¦  «        d…                              ¦   «         }|                      |¦  «        }| 	                    ¦   «         sdS 	 | j
                             | j        || j        ¬¦  «        ƒ d{V —†}|j        r	d| _        dS dS # t           $ r&}t"                               d|¦  «         Y d}~dS d}~ww xY w)aK  Deliver un-sent tail content before a segment-break reset.

        When an edit fails (flood control, transport error) and a tool
        boundary arrives before the next retry, ``_accumulated`` holds text
        that was generated but never shown to the user. Without this flush,
        the segment reset would discard that tail and leave a frozen cursor
        in the partial message.

        Sends the tail that sits after the last successfully-delivered
        prefix as a new message, and best-effort strips the stuck cursor
        from the previous partial message.
        NrÅ   Tz"Segment-break tail flush error: %s)r0   Ú_try_strip_cursorr1   r¸   r*   r»   rX   r†   r©   rd   r   r°   r   r!   r±   r,   r�   rŽ   r�   )r9   ÚvisibleÚtailr´   r    s        r   r‰   z9GatewayStreamConsumer._flush_segment_tail_on_edit_failure–  sv  è è € ð Ô(ð 	+Ø×(Ò(Ñ*Ô*Ð*Ð*Ð*Ð*Ð*Ð*Ð*ØÔ'ÐA¨4×+?Ò+?Ñ+AÔ+AˆØÔ ˆØð 	0�t—’ wÑ/Ô/ð 	0Ø�˜G™œ˜˜Ô&×-Ò-Ñ/Ô/ˆDØ×&Ò& tÑ,Ô,ˆØ�zŠz‰|Œ|ð 	ØˆFð		BØœ<×,Ò,ØœØØœð -ñ ô ð ð ð ð ð ð ˆFð
 Œ~ð *Ø%)�Ô"Ð"Ð"ð*ð *øåð 	Bð 	Bð 	BÝ�LŠLÐ=¸qÑAÔAÐAÐAÐAÐAÐAÐAÐAøøøøð	Bøøøs   Â2;C1 Ã1
D!Ã;DÄD!c              ƒ  ó  K  — | j         r| j         dk    rdS |                      ¦   «         }|r|                     ¦   «         sdS 	 | j                             | j        | j         |¬¦  «        ƒ d{V —† || _        dS # t          $ r Y dS w xY w)uÆ   Best-effort edit to remove the cursor from the last visible message.

        Called when entering fallback mode so the user doesn't see a stuck
        cursor (â–‰) in the partial message.
        rN   NrÄ   )r+   r¸   rd   r   rÆ   r   r/   r�   r¶   s     r   rØ   z'GatewayStreamConsumer._try_strip_cursor·  sÔ   è è € ð Ôð 	 4Ô#3°}Ò#DÐ#DØˆFØ×%Ò%Ñ'Ô'ˆØð 	˜VŸ\š\™^œ^ð 	ØˆFð	Ø”,×+Ò+ØœØÔ+Øð ,ñ ô ð ð ð ð ð ð ð ð
 $*ˆDÔ Ð Ð øÝð 	ð 	ð 	ØˆDˆDð	øøøs   Á4A: Á:
BÂBc              ƒ  ó,  K  — |                       |¦  «        }|                     ¦   «         sdS 	 | j                             | j        || j        ¬¦  «        ƒ d{V —†}|j        S # t          $ r&}t           	                    d|¦  «         Y d}~dS d}~ww xY w)z6Send a completed interim assistant commentary message.FrÅ   NzCommentary send error: %s)
r©   rd   r   r°   r   r!   r±   r�   rŽ   r�   )r9   rF   r´   r    s       r   rˆ   z&GatewayStreamConsumer._send_commentaryÌ  sÅ   è è € à×&Ò& tÑ,Ô,ˆØ�zŠz‰|Œ|ð 	Ø�5ð	Øœ<×,Ò,ØœØØœð -ñ ô ð ð ð ð ð ð ˆFð ”>Ð!øÝð 	ð 	ð 	Ý�LŠLÐ4°aÑ8Ô8Ð8Ø�5�5�5�5�5øøøøð	øøøs   ¯3A# Á#
BÁ-BÂBr{   r|   c             ƒ  óØ  K  — |                       |¦  «        }|}| j        j        r |                     | j        j        d¦  «        }|                     ¦   «         }|sdS |                     ¦   «         sdS d}| j        €/| j        j        r#| j        j        |v rt          |¦  «        |k     rdS 	 | j        ��n| j        �rd|| j        k    r|r| j	        sdS | j
                             | j        | j        ||¬¦  «        ƒ d{V —†}|j        rd| _        || _        d| _        dS |                      |¦  «        rƒ| xj        dz  c_        t#          | j        dz  d	¦  «        | _        t&                               d
| j        | j        | j        ¦  «         | j        | j        k     rt-          j        ¦   «         | _        dS t&                               d| j        ¦  «         |                      ¦   «         | _        d| _        d| _        d| _        |                      ¦   «         ƒ d{V —† dS dS | j
                             | j        || j        ¬¦  «        ƒ d{V —†}|j        rY|j        r|j        | _        nd| _        d| _        || _        |j        s'|                      ¦   «         | _        d| _        d| _        dS d| _        dS # t@          $ r&}t&           !                    d|¦  «         Y d}~dS d}~ww xY w)a\  Send or edit the streaming message.

        Returns True if the text was successfully delivered (sent or edited),
        False otherwise.  Callers like the overflow split loop use this to
        decide whether to advance past the delivered chunk.

        ``finalize`` is True when this is the last edit in a streaming
        sequence.
        r$   Té   N)r   r²   r­   r|   r   r]   rz   g      $@u@   Flood control on edit (strike %d/%d), backoff interval â†’ %.1fsFz0Edit failed (strikes=%d), entering fallback moderÅ   rN   zStream send/edit error: %s)"r©   r&   r   r£   rd   r+   rX   r-   r/   r6   r   rÆ   r   r±   r,   r2   rÇ   Úminr3   rŽ   rÈ   Ú_MAX_FLOOD_STRIKESr�   r‚   r.   r¸   r1   r0   rØ   r°   r!   r²   r�   r�   )r9   rF   r|   Úvisible_without_cursorÚ_visible_strippedÚ_MIN_NEW_MSG_CHARSr´   r    s           r   r…   z#GatewayStreamConsumer._send_or_editá  s¯  è è € ð ×&Ò& tÑ,Ô,ˆð "&ÐØŒ8Œ?ð 	YØ%;×%CÒ%CÀDÄHÄOÐUWÑ%XÔ%XÐ"Ø2×8Ò8Ñ:Ô:ÐØ ð 	Ø�4Ø�zŠz‰|Œ|ð 	Ø�4ð ÐØÔÐ$Ø”H”Oð %à”H”O tÐ+Ð+ÝÐ)Ñ*Ô*Ð-?Ò?Ð?Ø�4ða	ØÔÑ+ØÔ'ñ B!ð ˜tÔ3Ò3Ð3Ø ð 4Ø%)Ô%Dð 4ð  $˜tà#'¤<×#<Ò#<Ø $¤Ø#'Ô#3Ø $Ø!)ð	 $=ñ $ô $ð ð ð ð ð ð �Fð ”~ð ,%Ø-1˜Ô*Ø/3˜Ô,à./˜Ô+Ø#˜tð  ×/Ò/°Ñ7Ô7ð -Ø Ð/Ô/°1Ñ4Ð/Ô/Ý:=Ø $Ô ;¸aÑ ?Àñ;ô ;˜DÔ7õ #ŸLšLð!=à $Ô 3Ø $Ô 7Ø $Ô ;ñô ð ð  $Ô2°TÔ5LÒLÐLõ 8<´~Ñ7GÔ7G Ô 4Ø', uõ
 ŸšØNØ Ô/ñô ð ð 15×0DÒ0DÑ0FÔ0F˜Ô-Ø48˜Ô1Ø/4˜Ô,Ø-1˜Ô*ð #×4Ò4Ñ6Ô6Ð6Ð6Ð6Ð6Ð6Ð6Ð6Ø$˜uð !˜5ð  $œ|×0Ò0Ø œLØ Ø!œ]ð  1ñ  ô  ð ð ð ð ð ð �ð
 ”>ð !ØÔ(ð 5Ø+1Ô+<˜Ô(Ð(à/4˜Ô,Ø)-�DÔ&Ø+/�DÔ(Ø!Ô,ð 9Ø04×0DÒ0DÑ0FÔ0F˜Ô-Ø48˜Ô1ð ,9˜Ô(Ø˜4ð ,1�DÔ(Ø ˜5øÝð 	ð 	ð 	Ý�LŠLÐ5°qÑ9Ô9Ð9Ø�5�5�5�5�5øøøøð	øøøs:   Â-$J9 ÃA
J9 ÄBJ9 Æ7A(J9 È#BJ9 Ê0J9 Ê9
K)ËK$Ë$K))NN)r   r   r   r   r   r    r!   r"   )r;   r   )r;   rA   )rF   r   r;   rA   )rL   r   r;   rA   )rF   r   r;   r   )rF   r   rª   r«   r;   r«   )r;   r   )r¹   r   r;   r   )rF   r   r½   r   r;   r¾   )rF   r   r;   r   )rF   r   r|   r   r;   r   )"r   r   r   r   rà   ra   r^   r:   Úpropertyr>   r@   rE   rJ   rO   rQ   rT   rr   rt   r¡   r¦   Úcompiler¤   Ústaticmethodr©   r„   r¸   r¼   rÂ   r‡   rÇ   r‰   rØ   rˆ   r…   r   r   r   r   r   1   s€  € € € € € ðð ð  Ðð
ÐðÐð 26Ø#'ð" ð " ð " ð " ð " ðH ð"ð "ð "ñ „Xð"ð ð)ð )ð )ñ „Xð)ð&ð &ð &ð &ð1ð 1ð 1ð 1ð
 @Eð #ð #ð #ð #ð #ð #ð
$ð 
$ð 
$ð 
$ðð ð ð ðVð Vð Vð Vðp$ð $ð $ð $ðL9ð L9ð L9ð L9ðb �”
Ð:Ñ;Ô;€Iàð ð  ð  ñ „\ð ð&ð ð ð ð8/ð /ð /ð /ðð ð ð ð ðð ð ñ „\ðð b#ð b#ð b#ð b#ðHYð Yð Yð YðBð Bð Bð BðBð ð ð ð*ð ð ð ð* BGð Hð Hð Hð Hð Hð Hð Hð Hr   r   )r   Ú
__future__r   rŠ   Úloggingr'   r¦   r�   Údataclassesr   Útypingr   r   Ú	getLoggerrŽ   ÚobjectrS   rD   rH   r   r   r   r   r   ú<module>rí      s$  ððð ð #Ð "Ð "Ð "Ð "Ð "à €€€Ø €€€Ø €€€Ø 	€	€	€	Ø €€€Ø !Ð !Ð !Ð !Ð !Ð !Ø  Ð  Ð  Ð  Ð  Ð  Ð  Ð  à	ˆÔ	Ð4Ñ	5Ô	5€ð 	ˆ‰Œ€ð ˆv‰xŒx€ð ˆf‰hŒh€ð ðð ð ð ð ñ ô ñ „ððxð xð xð xð xñ xô xð xð xð xr   