+
     4Wj`                        R t ^ RIHt ^ RIt^ RIt^ RIt]P                  P                  RR4      t	]P                  P                  R]P                  P                  RR4      4      t
RtR	 R
 ltR R ltR# )u4  Auto-supersession — temporal versioning of memory.

When a new durable fact UPDATES one already stored (a client's ticket size
changed, a plan moved, a status flipped), we don't want two contradictory facts
sitting side by side — the latest should win while the old is kept as history
(queryable, never deleted). This asks the LOCAL model whether the new fact
replaces one of the few most-similar existing facts and returns that fact's id
(else None); the engine then calls /supersede instead of /remember. Free (local
model). MEM_SUPERSEDE=0 disables.

FALLBACK PATH ONLY: the memory service now owns dedup/supersede inside its own
server-side write pipeline, so this client-side round-trip runs only when
MEM_SERVER_PIPELINE=0 is set (see voice/memory_control.py fire_save). Kept
importable for that escape hatch.
)annotationsN
OLLAMA_URLzhttp://127.0.0.1:11434MEM_EXTRACT_MODELLOCAL_MODELzqwen3:4b-instructui  A new fact about Ahmed just arrived. Decide if it is an UPDATE that REPLACES one of the existing facts below — i.e. it describes the SAME thing (same person/company and same attribute) but with a CHANGED or newer value or status. It is NOT a replacement if it is merely related, additional, or about something else — only say it replaces one when the new value genuinely supersedes the old.

New fact: "{new}"

Existing facts (id: text):
{cands}

If the new fact replaces exactly ONE existing fact, output ONLY that fact's id.
If it replaces none, output exactly: NONE
Output only the id or NONE — nothing else.c                    V ^8  d   QhRRRR/# )   promptstrreturn )formats   "voice/memory_supersede.py__annotate__r   *   s            c                    ^ RI Hp VP                  4       '       d>   VP                  V \        P
                  P                  RVP                  4      R^R7      #   \        P                  ! R\        RRRR	R
RV /.RR^ //4      P                  4       p\        P                  P                  \          R2VRR/R7      p\        P                  P#                  V^R7      ;_uu_ 4       p\        P$                  ! VP'                  4       4      pRRR4       XP                  R4      ;'       g    / P                  R4      ;'       g    RP)                  4       #   \         d     EL	i ; i  + '       g   i     Ld; i  \         d     R# i ; i)zJudge with DeepSeek V4 Flash (smart + cheap) if a key is set, else the
local model. Judging supersession needs more nuance than qwen3:4b has.)deepseekMEM_JUDGE_MODEL   )model
max_tokenstimeoutr   streamFmessagesroleusercontentoptionstemperaturez	/api/chatzContent-Typezapplication/json)dataheaders)r   Nmessage )voicer   	availablechatosenvirongetFLASH	ExceptionjsondumpsMODELencodeurllibrequestRequestOLLAMAurlopenloadsreadstrip)r   r   bodyreqros   &     r   _askr:   *   sb   "==bjjnn->O ! , ,  zzUHe&&)V<=q)
  68	 	
 nn$$xy%9.<>P-Q % S^^##C#44

1668$A 5y!''R,,Y7==2DDFF   54  sM   E <E BE< (%E)E< -E< E< E&%E&)E9	4E< <F
Fc               $    V ^8  d   QhRRRRRR/# )r   new_textr	   
candidatesz
list[dict]r
   z
str | Noner   )r   s   "r   r   r   D   s!      c z j r   c                   \         P                  P                  RR4      R8X  d   R# T ;'       g    RP                  4       p T;'       g    .  Uu. uF6  q"P                  R4      '       g   K  VP                  R4      '       g   K4  VNK8  	  upR,          p\	        V 4      ^8  g	   V'       g   R# V Uu0 uF  q"R,          kK  	  ppR	P                  R
 V 4       4      p\        P                  WR7      p\        V4      pV'       g   R# VP                  RR4      P                  RR4      P                  RR4      P                  4        F  pW9   g   K  Vu # 	  R# u upi u upi )zAReturn the id of the existing fact the new one replaces, or None.MEM_SUPERSEDE10Nr!   idfact:N   N
c              3  J   "   T F  qR ,           RVR,           2x  K  	  R# 5i)rB   z: rC   Nr   ).0cs   & r   	<genexpr>"find_superseded.<locals>.<genexpr>M   s!     A5aT7)2ai[15s   !#)newcands" ':)r%   r&   r'   r5   lenjoin_PROMPTr   r:   replacesplit)	r<   r=   rH   rL   idslistingr   anstoks	   &&       r   find_supersededrZ   D   s   	zz~~os+s2B%%'H#))r)L)1eeDkQaeeFmQQ)LRPE
8}q!
"EqT77EC
"iiA5AAG^^^8F
v,C{{3$,,S#6>>sCHNNP:J Q  M #s   E-EE4E")__doc__
__future__r   r*   r%   urllib.requestr.   r&   r'   r1   r,   rS   r:   rZ   r   r   r   <module>r^      sg    #  	 	&>	?


*zz~~m5HI	K0 4r   