
    Ojg                       d Z ddlmZ ddlZddlZddlZddlZej                  j                  dd      Z
ej                  j                  dej                  j                  dd            Zd	Zh d
Zh dZddZddZddZy)u`  Layer 2 — entity extractor (the local NER brain).

Pulls the people / companies / products / amounts out of a durable fact using
the LOCAL model (Ollama) — the same model the passive extractor already runs,
so it costs nothing extra and never touches an API. The memory service turns
whatever we return into shared :Entity nodes and (:Memory)-[:MENTIONS]->(:Entity)
edges, so "pull up Al Temimi" returns everything hanging off that one node.

Called from memory_control.fire_save() in the background save thread, and by the
one-off entity backfill over memories saved before Layer 2. MEM_ENTITIES=0 off.
    )annotationsN
OLLAMA_URLzhttp://127.0.0.1:11434MEM_EXTRACT_MODELLOCAL_MODELzqwen3:4b-instructa  Extract the named entities from this fact about Ahmed's work and life. List the specific PEOPLE, COMPANIES/clients, PRODUCTS, PLACES, and AMOUNTS (money or notable numbers) it names by their real name. Skip generic words like "client", "meeting", "the company", "a lead" unless a real name is given.

Output ONLY a JSON array; each item {{"name": "...", "type": "..."}} where type is one of: person, company, product, place, amount. If there are no real named entities, output exactly [].

Fact: "{text}"

JSON:>   placeamountpersoncompanyproduct>   a leada client	a company
the clienthis companythe companytwo clientshemehimsirleadahmedclientjarvisclientsr
   meetingcustomeremployee	customers	employeesc                    | j                         j                         }|t        v ryt        t	        j
                  d|            S )NTz(a|an|the|one|two|three|four|five|\d+)?\s*(client|clients|lead|leads|customer|customers|company|companies|meeting|meetings|employee|employees))lowerstrip_SKIPboolre	fullmatch)namens     C/Users/ahmed/devFolder/Ultron/claude-voice/voice/memory_entities.py_is_genericr+   /   s@    

AEz	0124 5 5    c                   t        j                  t        ddt        j	                  |       dgddid      j                         }t        j                  j                  t         d|d	d
i      }t        j                  j                  |d      5 }t        j                  |j                               }d d d        j                  d      xs i j                  d      xs dj                         S # 1 sw Y   ?xY w)NFuser)text)rolecontenttemperaturer   )modelstreammessagesoptionsz	/api/chatzContent-Typezapplication/json)dataheaders   )timeoutmessager1    )jsondumpsMODEL_PROMPTformatencodeurllibrequestRequestOLLAMAurlopenloadsreadgetr#   )r/   bodyreqros        r*   _postrO   :   s    ::%$T1JKL!1%  vx	 	
 ..
 
 F89!5D*8:L)M ! OC			R		0AJJqvvx  
1UU9#((39r@@BB 
1	0s   $C55C>c                z   | xs dj                         } t        j                  j                  dd      dk(  st	        |       dk  rg S 	 t        |       }t        j                  d|t        j                        }|sg S 	 t        j                  |j                  d            }g t               }}t        |t              r|ng D ]  }t        |t               st#        |j                  dd            j                         }t	        |      d	k  st%        |      rWt#        |j                  d
d            j                         j'                         }|t(        vrd}|j'                         }	|	|v r|j+                  |	       |j-                  ||d        |dd S # t        $ r g cY S w xY w# t        $ r g cY S w xY w)zIReturn [{"name","type"}] entities from `text`; [] on anything unexpected.r<   MEM_ENTITIES10   z\[.*\]r   r(      typething)r(   rV   N   )r#   osenvironrJ   lenrO   	Exceptionr&   searchDOTALLr=   rH   groupset
isinstancelistdictstrr+   r"   _TYPESaddappend)
r/   rawmr7   outseener(   etypekeys
             r*   extractro   G   s   JBD	zz~~nc*c1SY]	Dk 			)S")),A	zz!''!*% CECd+T3!T"155$%++-t9q=K-AEE&'*+11399;Ejjl$;

D%01 4 s8O1  	  	s$   	F >$F, F)(F),F:9F:)r(   rd   returnr%   )r/   rd   rp   rd   )r/   rd   rp   z
list[dict])__doc__
__future__r   r=   rY   r&   urllib.requestrC   rZ   rJ   rF   r?   r@   re   r$   r+   rO   ro    r,   r*   <module>ru      s   
 #  	 	 	&>	?


*zz~~m5HI	K	 
=	M5
Cr,   