Ë
    uPêj&*  ã                  ó  — U d Z ddlmZ ddlZddlmZmZ ddlmZm	Z	 ddl
mZmZmZmZmZ e G d„ d«      «       Ze G d	„ d
«      «       Ze G d„ d«      «       Z ej&                  d«      Zded<   d,d„Zd-d„Z ej&                  dej0                  «       ej&                  dej0                  «       ej&                  dej0                  «      gZded<    ej&                  dej0                  «       ej&                  dej0                  «       ej&                  dej0                  «      gZded<   d.d„Zd/d„Zd0d„Zd0d„Zd1d„Zd2d„Z d3d „Z!h d!£Z"ded"<   d#Z#ded$<   d4d%„Z$	 	 	 	 	 	 	 	 	 	 d5d&„Z%	 	 	 	 	 	 	 	 	 	 	 	 	 	 d6d'„Z&d7d(„Z'd8d)„Z(d9d*„Z)d:d+„Z*y);a÷  
Incremental signal detection for the adaptive router.

Each session maintains a SessionState. On every turn, we call apply_turn(state, turn)
which mutates the state in place and returns a SignalDelta listing which signals
fired on THIS turn. The router then queues the delta to be flushed to DB.

Design constraint: O(1) work per turn. No re-scanning the full session history.
We keep small bounded windows: last_user_content, last_assistant_content, and a
bounded list of recent tool call signatures.
é    )ÚannotationsN)Ú	dataclassÚfield)ÚAnyÚFinal)ÚLOOP_REPEAT_THRESHOLDÚMIN_TURNS_FOR_CLEAN_CREDITÚMISALIGNMENT_JACCARD_THRESHOLDÚSTAGNATION_JACCARD_NEAR_DUPÚTOOL_CALL_HISTORY_MAXc                  ó|   — 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Z	ded<   dZ
ded	<   dZded
<   dd„Zy)ÚSignalDeltazMWhich signals fired on a single turn. Counts are 0 or 1 (one delta per turn).r   ÚintÚmisalignmentÚ
stagnationÚdisengagementÚsatisfactionÚfailureÚloopÚ
exhaustionc           	     ó²   — t        | j                  | j                  | j                  | j                  | j
                  | j                  | j                  g«      S ©N)Úanyr   r   r   r   r   r   r   )Úselfs    út/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/router_strategy/adaptive_router/signals.pyÚ	any_firedzSignalDelta.any_fired*   sK   € Üà×!Ñ!Ø—‘Ø×"Ñ"Ø×!Ñ!Ø—‘Ø—	‘	Ø—‘ðó

ð 
	
ó    N)ÚreturnÚbool)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   Ú__annotations__r   r   r   r   r   r   r   © r   r   r   r      sO   … áWà€L�#ÓØ€J�ÓØ€M�3ÓØ€L�#ÓØ€GˆSÓØ€Dˆ#ƒMØ€J�Óô
r   r   c                  ó(  — e Zd ZU dZded<   ded<   ded<   ded<   dZded	<   dZded
<   dZded<   dZded<   dZ	ded<   dZ
ded<   dZded<   dZded<   dZded<    ee¬«      Zded<    ee¬«      Zded<   dZded<   dZded<   dZded<   dZded <   y)!ÚSessionStateuÏ   In-memory rolling state for one session.

    Mirrors the LiteLLM_AdaptiveRouterSession DB row (Wave 0 schema). The flusher
    later persists this. We keep this as a plain dataclass â€” no DB coupling.
    ÚstrÚ
session_idÚrouter_nameÚ
model_nameÚclassified_typer   r   Úmisalignment_countÚstagnation_countÚdisengagement_countÚsatisfaction_countÚfailure_countÚ
loop_countÚexhaustion_countNú
str | NoneÚlast_user_contentÚlast_assistant_content©Údefault_factoryú	list[str]Útool_call_historyzdict[str, str]Úpending_tool_callsÚ
turn_countéÿÿÿÿÚlast_processed_turnFr   Úclean_credit_awardedú
int | NoneÚterminal_status)r    r!   r"   r#   r$   r-   r.   r/   r0   r1   r2   r3   r5   r6   r   Úlistr:   Údictr;   r<   r>   r?   rA   r%   r   r   r'   r'   8   sÊ   … ñð ƒOØÓØƒOØÓàÐ˜ÓØÐ�cÓØ Ð˜Ó ØÐ˜ÓØ€M�3ÓØ€J�ÓØÐ�cÓà$(Ð�zÓ(Ø)-Ð˜JÓ-Ù#(¸Ô#>Ð�yÓ>Ù).¸tÔ)DÐ˜ÓDà€J�ÓØ!Ð˜Ó!Ø!&Ð˜$Ó&Ø"&€O�ZÔ&r   r'   c                  ót   — e Zd ZU dZdZded<   dZded<    ee¬«      Z	ded<    ee¬«      Z
ded	<   dZd
ed<   y)ÚTurnzCOne turn of input. Caller assembles this from the request/response.Nr4   Úuser_contentÚassistant_contentr7   úlist[dict[str, Any]]Ú
tool_callsÚtool_resultsr@   Úresponse_status)r    r!   r"   r#   rF   r$   rG   r   rB   rI   rJ   rK   r%   r   r   rE   rE   X   sD   … áMà#€L�*Ó#Ø$(Ð�zÓ(Ù',¸TÔ'B€JÐ$ÓBÙ).¸tÔ)D€LÐ&ÓDØ"&€O�ZÔ&r   rE   z[A-Za-z0-9]+r   Ú	_TOKEN_REc                ó†   — | s
t        «       S t        j                  | «      D �ch c]  }|j                  «       ’Œ c}S c c}w r   )ÚsetrL   ÚfindallÚlower)ÚtextÚts     r   Ú_tokensrS   h   s1   € ÙÜ‹uˆÜ(×0Ñ0°Ó6Ö7˜!ˆA�G‰G�IÒ7Ð7ùÒ7s   ¤>c                óF   — | |z  }|syt        | |z  «      t        |«      z  S )Nç        )Úlen)ÚaÚbÚunions      r   Ú_jaccardrZ   n   s(   € Ø�q‘5€EÙØÜˆq�1‰u‹:œ˜E›
Ñ"Ð"r   z>\b(forget it|never mind|give up|talk to (?:a )?human|cancel)\bz/\b(this (?:isn'?t|is not) working|stop|abort)\bz#\bi'?ll do it (?:myself|manually)\bÚ_DISENGAGEMENT_PATTERNSz?\b(that worked|that did it|works now|fixed it|solved it|nice)\bz4\b(thanks|thank you|thx|appreciated|appreciate it)\bz%\b(perfect|great|excellent|exactly)\bÚ_SATISFACTION_PATTERNSc                óp   — | r|syt        t        | «      t        |«      «      }d|cxk  xr	 t        k  S c S )uÂ   Fires when consecutive user messages share *some* topic (jaccard > 0)
    but are sufficiently different (jaccard < threshold) â€” i.e. user is
    rephrasing, not changing topic, not repeating.FrU   )rZ   rS   r
   )Ú	prev_userÚ	curr_userÚjs      r   Ú_detect_misalignmentra   …   s9   € ñ ™IØÜœ 	Ó*¬G°IÓ,>Ó?€AØ�Ö3Ô3Ñ3Ð3Ñ3Ð3r   c                óZ   — | r|syt        t        | «      t        |«      «      }|t        k\  S )z>Fires when consecutive assistant messages are near-duplicates.F)rZ   rS   r   )Ú	prev_asstÚ	curr_asstr`   s      r   Ú_detect_stagnationre   �   s,   € á™IØÜœ 	Ó*¬G°IÓ,>Ó?€AØÔ+Ñ+Ð+r   c                ó:   ‡ — ‰ syt        ˆ fd„t        D «       «      S )NFc              3  ó@   •K  — | ]  }|j                  ‰«      –— Œ y ­wr   ©Úsearch©Ú.0Úpr_   s     €r   ú	<genexpr>z(_detect_disengagement.<locals>.<genexpr>š   s   øè ø€ ÒD qˆq�x‰x˜	×"ÑDùó   ƒ)r   r[   ©r_   s   `r   Ú_detect_disengagementrp   —   s   ø€ ÙØÜÓDÔ,CÔDÓDÐDr   c                ó:   ‡ — ‰ syt        ˆ fd„t        D «       «      S )NFc              3  ó@   •K  — | ]  }|j                  ‰«      –— Œ y ­wr   rh   rj   s     €r   rm   z'_detect_satisfaction.<locals>.<genexpr>    s   øè ø€ ÒC qˆq�x‰x˜	×"ÑCùrn   )r   r\   ro   s   `r   Ú_detect_satisfactionrs   �   s   ø€ ÙØÜÓCÔ,BÔCÓCÐCr   c                ó8   — | D ]  }|j                  d«      sŒ y y)u  Any tool result explicitly flagged as an error.

    We do NOT treat empty content as failure â€” many tools legitimately return
    empty output (zero-result searches, silent bash commands, void writes) and
    penalizing the model for those would corrupt the bandit posterior.
    Úis_errorTF)Úget)rJ   Úrs     r   Ú_detect_failurerx   £   s'   € ð ò ˆØ�5‰5�ÕÙðð r   c                ój  ‡— | j                  d«      xs" | j                  di «      j                  dd«      }| j                  d«      Š‰€"| j                  di «      j                  dd«      Št        ‰t        «      r1dj                  ˆfd„t	        ‰j                  «       «      D «       «      Š|› d‰› d�S )	zAStable signature for loop detection: name + sorted JSON-ish args.ÚnameÚfunctionÚ Ú	argumentsú,c              3  ó2   •K  — | ]  }|› d ‰|   › �–— Œ y­w)ú=Nr%   )rk   ÚkÚ	call_argss     €r   rm   z_signature.<locals>.<genexpr>·   s    øè ø€ ÒU°q ˜s ! I¨a¡L >Ô2ÑUùs   ƒú(ú))rv   Ú
isinstancerC   ÚjoinÚsortedÚkeys)Úcallrz   r‚   s     @r   Ú
_signaturerŠ   °   sœ   ø€ à—(‘(˜6Ó"ÒN d§h¡h¨z¸2Ó&>×&BÑ&BÀ6È2Ó&N€DØ—‘˜Ó%€IØÐØ—H‘H˜Z¨Ó,×0Ñ0°¸bÓAˆ	Ü�)œTÔ"Ø—H‘HÓU¼FÀ9Ç>Á>ÓCSÓ<TÔUÓUˆ	ØˆV�1�Y�K˜qÐ!Ð!r   c                ól   — |sy|D ],  }t        |«      }| j                  |«      }|t        dz
  k\  sŒ, y y)z�Fires if any new call's signature appears >= LOOP_REPEAT_THRESHOLD-1 times
    in recent history (so this call would be the Nth).Fé   T)rŠ   Úcountr   )ÚhistoryÚ	new_callsr‰   ÚsigÚrecent_counts        r   Ú_detect_loopr’   »   sG   € ñ ØØò ˆÜ˜ÓˆØ—}‘} SÓ)ˆØÔ0°1Ñ4Ó4Ùð	ð
 r   >   é˜  é�  é­  é÷  éø  Ú_EXHAUSTION_STATUSES)zcontext lengthzcontext windowztoken limitz
rate limitztoo many requestsÚtimeoutÚ_EXHAUSTION_KEYWORDSc                ó°   ‡— | �	| t         v ry|D ]E  }t        |j                  dd«      «      j                  «       Št	        ˆfd„t
        D «       «      sŒE y y)NTÚcontentr|   c              3  ó&   •K  — | ]  }|‰v –— Œ
 y ­wr   r%   )rk   Úkwrœ   s     €r   rm   z%_detect_exhaustion.<locals>.<genexpr>Ù   s   øè ø€ Ò< ˆr�WŒ}Ñ<ùs   ƒF)r˜   r(   rv   rP   r   rš   )ÚstatusrJ   rw   rœ   s      @r   Ú_detect_exhaustionr    Ô   sX   ø€ ØÐ˜fÔ(<Ñ<ØØò ˆÜ�a—e‘e˜I rÓ*Ó+×1Ñ1Ó3ˆÜÓ<Ô';Ô<Õ<Ùðð r   c           
     óº   — t        t        t        | |«      «      t        t        |«      «      t        |xr t	        |«      «      t        t        |«      «      ¬«      S )N)r   r   r   r   )r   r   ra   rp   rs   rx   )Úprevious_user_contentÚcurrent_user_contentrJ   Úallow_satisfactions       r   Údetect_user_feedbackr¥   Þ   sT   € ô ÜÔ-Ð.CÐEYÓZÓ[ÜÔ/Ð0DÓEÓFÜÐ+ÒZÔ0DÐEYÓ0ZÓ[Ü”O LÓ1Ó2ô	ð r   c           
     ó�   — t        t        t        | |«      «      t        t        ||«      «      t        t	        ||«      «      ¬«      S )N)r   r   r   )r   r   re   r’   r    )Úprevious_assistant_contentÚcurrent_assistant_contentr:   rI   rJ   rK   s         r   Údetect_response_signalsr©   ì   sI   € ô ÜÜØ*Ø)óó
ô ”Ð/°Ó<Ó=ÜÔ)¨/¸<ÓHÓIô	ð 	r   c                 ó  — t        t        d„ | D «       «      t        d„ | D «       «      t        d„ | D «       «      t        d„ | D «       «      t        d„ | D «       «      t        d„ | D «       «      t        d„ | D «       «      ¬«      S )	Nc              3  ó4   K  — | ]  }|j                   –— Œ y ­wr   )r   ©rk   Údeltas     r   rm   z&merge_signal_deltas.<locals>.<genexpr>  ó   è ø€ Ò@°˜×+Õ+Ñ@ùó   ‚c              3  ó4   K  — | ]  }|j                   –— Œ y ­wr   )r   r¬   s     r   rm   z&merge_signal_deltas.<locals>.<genexpr>  ó   è ø€ Ò<¨E�u×'Õ'Ñ<ùr¯   c              3  ó4   K  — | ]  }|j                   –— Œ y ­wr   )r   r¬   s     r   rm   z&merge_signal_deltas.<locals>.<genexpr>  s   è ø€ ÒB°%˜%×-Õ-ÑBùr¯   c              3  ó4   K  — | ]  }|j                   –— Œ y ­wr   )r   r¬   s     r   rm   z&merge_signal_deltas.<locals>.<genexpr>  r®   r¯   c              3  ó4   K  — | ]  }|j                   –— Œ y ­wr   )r   r¬   s     r   rm   z&merge_signal_deltas.<locals>.<genexpr>  s   è ø€ Ò6 e�E—M•MÑ6ùr¯   c              3  ó4   K  — | ]  }|j                   –— Œ y ­wr   )r   r¬   s     r   rm   z&merge_signal_deltas.<locals>.<genexpr>  s   è ø€ Ò0 �—•Ñ0ùr¯   c              3  ó4   K  — | ]  }|j                   –— Œ y ­wr   )r   r¬   s     r   rm   z&merge_signal_deltas.<locals>.<genexpr>  r±   r¯   )r   r   r   r   r   r   r   )r   Úsum)Údeltass    r   Úmerge_signal_deltasr¹      sq   € ÜÜÑ@¸Ô@Ó@ÜÑ<°VÔ<Ó<ÜÑB¸6ÔBÓBÜÑ@¸Ô@Ó@ÜÑ6¨vÔ6Ó6ÜÑ0¨Ô0Ó0ÜÑ<°VÔ<Ó<ôð r   c                ó¶  — | xj                   |j                  z  c_         | xj                  |j                  z  c_        | xj                  |j
                  z  c_        | xj                  |j                  z  c_        | xj                  |j                  z  c_        | xj                  |j                  z  c_
        | xj                  |j                  z  c_        y r   )r-   r   r.   r   r/   r   r0   r   r1   r   r2   r   r3   r   )Ústater­   s     r   Úapply_signal_deltar¼     sŸ   € Ø	×Ò × 2Ñ 2Ñ2ÕØ	×Ò˜e×.Ñ.Ñ.ÕØ	×Ò ×!4Ñ!4Ñ4ÕØ	×Ò × 2Ñ 2Ñ2ÕØ	×Ò˜5Ÿ=™=Ñ(ÕØ	×Ò˜Ÿ
™
Ñ"ÕØ	×Ò˜e×.Ñ.Ñ.Ör   c                óÒ  — |j                   r|j                   | _        |j                  r|j                  | _        |j                  D ]&  }| j
                  j                  t        |«      «       Œ( t        | j
                  «      t        kD  r| j
                  t         d  | _        |j                  �|j                  | _        | xj                  dz  c_        | j                  | _        y )NrŒ   )rF   r5   rG   r6   rI   r:   ÚappendrŠ   rV   r   rK   rA   r<   r>   )r»   Úturnr‰   s      r   Úadvance_session_staterÀ     sÃ   € Ø×ÒØ"&×"3Ñ"3ˆÔØ×ÒØ'+×'=Ñ'=ˆÔ$à—‘ò 9ˆØ×Ñ×&Ñ&¤z°$Ó'7Õ8ð9ä
ˆ5×"Ñ"Ó#Ô&;Ò;Ø"'×"9Ñ"9Ô;PÐ:PÐ:QÐ"RˆÔà×ÑÐ'Ø $× 4Ñ 4ˆÔà	×Ò˜ÑÕØ %× 0Ñ 0€EÕr   c                ó¬  — t        | j                  |j                  |j                  | j                   xr | j
                  dz   t        k\  ¬«      }t        | j                  |j                  | j                  |j                  |j                  |j                  «      }t        ||«      }t        | |«       |j                  rd| _        t!        | |«       |S )zè
    Detect signals on this turn, mutate state, return the delta.

    O(1) per turn (no full-history rescan). Only inspects last_*, recent tool history
    (which is bounded at TOOL_CALL_HISTORY_MAX), and the new turn payload.
    rŒ   )r¤   T)r¥   r5   rF   rJ   r?   r<   r	   r©   r6   rG   r:   rI   rK   r¹   r¼   r   rÀ   )r»   r¿   Úfeedback_deltaÚresponse_deltar­   s        r   Ú
apply_turnrÄ   +  sÊ   € ô 1Ø×ÑØ×ÑØ×ÑØ %× :Ñ :Ð:Òq¸u×?OÑ?OÐRSÑ?SÔWqÑ?qô	€Nô 4Ø×$Ñ$Ø×ÑØ×ÑØ�‰Ø×ÑØ×Ñó€Nô 'ØØó€Eô �u˜eÔ$Ø×ÒØ%)ˆÔ"Ü˜% Ô&à€Lr   )rQ   r4   r   úset[str])rW   rÅ   rX   rÅ   r   Úfloat)r^   r4   r_   r4   r   r   )rc   r4   rd   r4   r   r   )r_   r4   r   r   )rJ   rH   r   r   )r‰   zdict[str, Any]r   r(   )rŽ   r9   r�   rH   r   r   )rŸ   r@   rJ   rH   r   r   )
r¢   r4   r£   r4   rJ   rH   r¤   r   r   r   )r§   r4   r¨   r4   r:   r9   rI   rH   rJ   rH   rK   r@   r   r   )r¸   r   r   r   )r»   r'   r­   r   r   ÚNone)r»   r'   r¿   rE   r   rÇ   )r»   r'   r¿   rE   r   r   )+r#   Ú
__future__r   ÚreÚdataclassesr   r   Útypingr   r   Ú.litellm.router_strategy.adaptive_router.configr   r	   r
   r   r   r   r'   rE   ÚcompilerL   r$   rS   rZ   Ú
IGNORECASEr[   r\   ra   re   rp   rs   rx   rŠ   r’   r˜   rš   r    r¥   r©   r¹   r¼   rÀ   rÄ   r%   r   r   ú<module>rÏ      s
  ðò
õ #ã 	ß (ß ÷õ ð ÷
ð 
ó ð
ð2 ÷'ð 'ó ð'ð> ÷'ð 'ó ð'ð �2—:‘:˜oÓ.€	ˆ5Ó .ó8ó#ð €B‡J�JÐPÐRT×R_ÑR_Ó`Ø€B‡J�JÐAÀ2Ç=Á=ÓQØ€B‡J�JÐ5°r·}±}ÓEð"Ð ˜ó ð €B‡J�JØJØ
�‰óð €B‡J�JÐFÈÏÉÓVØ€B‡J�JÐ7¸¿¹ÓGð!Ð ˜ó ó4ó,óEóDó
ó"ó
ò 8Ð �eÓ 7ðÐ �eó óðØ%ðà$ðð 'ðð ð	ð
 óðØ *ðà)ðð !ðð %ð	ð
 'ðð  ðð óó(	ó/ó1ô*r   