
    `gj]                     6   d Z ddlZddlZddlZddlmZ 	 ddlmZ dZ
dZdZd	Zd
Z ej                  d      Z ej                  d      Z ej                  d      Z ej                  dej&                        ZdedefdZ G d d      Zy# e	$ r ddlZY {w xY w)zl
SQLite-backed fact store with entity resolution and trust scoring.
Single-user Hermes memory store plugin.
    N)Path   )holographica|  
CREATE TABLE IF NOT EXISTS facts (
    fact_id         INTEGER PRIMARY KEY AUTOINCREMENT,
    content         TEXT NOT NULL UNIQUE,
    category        TEXT DEFAULT 'general',
    tags            TEXT DEFAULT '',
    trust_score     REAL DEFAULT 0.5,
    retrieval_count INTEGER DEFAULT 0,
    helpful_count   INTEGER DEFAULT 0,
    created_at      TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at      TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    hrr_vector      BLOB
);

CREATE TABLE IF NOT EXISTS entities (
    entity_id   INTEGER PRIMARY KEY AUTOINCREMENT,
    name        TEXT NOT NULL,
    entity_type TEXT DEFAULT 'unknown',
    aliases     TEXT DEFAULT '',
    created_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE IF NOT EXISTS fact_entities (
    fact_id   INTEGER REFERENCES facts(fact_id),
    entity_id INTEGER REFERENCES entities(entity_id),
    PRIMARY KEY (fact_id, entity_id)
);

CREATE INDEX IF NOT EXISTS idx_facts_trust    ON facts(trust_score DESC);
CREATE INDEX IF NOT EXISTS idx_facts_category ON facts(category);
CREATE INDEX IF NOT EXISTS idx_entities_name  ON entities(name);

CREATE VIRTUAL TABLE IF NOT EXISTS facts_fts
    USING fts5(content, tags, content=facts, content_rowid=fact_id);

CREATE TRIGGER IF NOT EXISTS facts_ai AFTER INSERT ON facts BEGIN
    INSERT INTO facts_fts(rowid, content, tags)
        VALUES (new.fact_id, new.content, new.tags);
END;

CREATE TRIGGER IF NOT EXISTS facts_ad AFTER DELETE ON facts BEGIN
    INSERT INTO facts_fts(facts_fts, rowid, content, tags)
        VALUES ('delete', old.fact_id, old.content, old.tags);
END;

CREATE TRIGGER IF NOT EXISTS facts_au AFTER UPDATE ON facts BEGIN
    INSERT INTO facts_fts(facts_fts, rowid, content, tags)
        VALUES ('delete', old.fact_id, old.content, old.tags);
    INSERT INTO facts_fts(rowid, content, tags)
        VALUES (new.fact_id, new.content, new.tags);
END;

CREATE TABLE IF NOT EXISTS memory_banks (
    bank_id    INTEGER PRIMARY KEY AUTOINCREMENT,
    bank_name  TEXT NOT NULL UNIQUE,
    vector     BLOB NOT NULL,
    dim        INTEGER NOT NULL,
    fact_count INTEGER DEFAULT 0,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
g?g        g      ?z$\b([A-Z][a-z]+(?:\s+[A-Z][a-z]+)+)\bz	"([^"]+)"z	'([^']+)'z;(\w+(?:\s+\w+)*)\s+(?:aka|also known as)\s+(\w+(?:\s+\w+)*)valuereturnc                 >    t        t        t        t        |             S N)max
_TRUST_MINmin
_TRUST_MAX)r   s    S/root/.hermes/venv/lib/python3.12/site-packages/plugins/memory/holographic/store.py_clamp_trustr   ^   s    z3z5122    c                      e Zd ZU dZi Zeed<    ej                         Z		 	 	 d*ddde
deddfd	Zd+d
Z	 	 d,dedededefdZ	 	 	 d-dededz  de
dedee   f
dZ	 	 	 	 d.dededz  de
dz  dedz  dedz  defdZdedefdZ	 	 	 d/dedz  de
dedee   fdZdededefdZdedee   fdZdedefdZdededdfdZdededdfd Zdeddfd!Zd0d"edz  defd#Zd$ej<                  defd%Zd+d&Z d1d'Z!d(e"ddfd)Z#y)2MemoryStorezBSQLite-backed fact store with entity resolution and trust scoring._sharedNdb_pathzstr | Path | Nonedefault_trusthrr_dimr   c                 ,   |ddl m} t         |       dz        }t        |      j	                         | _        | j
                  j                  j                  dd       t        |      | _	        || _
        t        j                  | _        	 t        | j
                  j                               | _        t"        j$                  5  t"        j&                  j)                  | j                        }|nt+        j,                  | j                  ddd       }t*        j.                  |_        |t3        j4                         ddd	}|t"        j&                  | j                  <   |d
xx   dz  cc<   || _        |d   | _        |d   | _        d d d        | j:                  5  | j6                  d   s| j=                          d| j6                  d<   d d d        y # t         $ r t        | j
                        | _        Y Dw xY w# 1 sw Y   wxY w# 1 sw Y   y xY w)Nr   )get_hermes_homezmemory_store.dbT)parentsexist_okFg      $@)check_same_threadtimeoutisolation_level)connlockrefsreadyr!   r   r   r    r"   )hermes_constantsr   strr   
expanduserr   parentmkdirr   r   r   hrr
_HAS_NUMPY_hrr_availableresolve_keyOSErrorr   _shared_guardr   getsqlite3connectRowrow_factory	threadingRLock_entry_conn_lock_init_db)selfr   r   r   r   entryr   s          r   __init__zMemoryStore.__init__t   s    ?8/+.??@GG}//1!!$!>)-8!nn	*DLL0023DI && 	'''++DII6E}II&+ 
 %)	 $+;; !%y/@!V[\16##DII.&MQMDKvDJvDJ'	', ZZ 	,;;w''+G$	, 	,1  	*DLL)DI	*	' 	',	, 	,s+   (G CG>/H
#G;:G;>H
Hc                 d   ddl m}  || j                  d       | j                  j                  t               | j                  j                  d      j                         D ch c]  }|d   	 }}d|vr| j                  j                  d       | j                  j                          y	c c}w )
zKCreate tables, indexes, and triggers if they do not exist. Enable WAL mode.r   )apply_wal_with_fallbackzmemory_store.db (holographic))db_labelzPRAGMA table_info(facts)r   
hrr_vectorz,ALTER TABLE facts ADD COLUMN hrr_vector BLOBN)hermes_stater>   r7   executescript_SCHEMAexecutefetchallcommit)r:   r>   rowcolumnss       r   r9   zMemoryStore._init_db   s    
 	9

5TU

  )%)ZZ%7%78R%S%\%\%^_c3q6__w&JJMN

 `s   %B-contentcategorytagsc                    | j                   5  |j                         }|st        d      	 | j                  j	                  d|||| j
                  f      }| j                  j                          |j                  }| j                  |      D ]%  }| j                  |      }| j                  ||       ' | j                  ||       | j!                  |       |cddd       S # t        j                  $ rE | j                  j	                  d|f      j                         }t        |d         cY cddd       S w xY w# 1 sw Y   yxY w)zInsert a fact and return its fact_id.

        Deduplicates by content (UNIQUE constraint). On duplicate, returns
        the existing fact_id without modifying the row. Extracts entities from
        the content and links them to the fact.
        zcontent must not be emptyz
                    INSERT INTO facts (content, category, tags, trust_score)
                    VALUES (?, ?, ?, ?)
                    z+SELECT fact_id FROM facts WHERE content = ?fact_idN)r8   strip
ValueErrorr7   rD   r   rF   	lastrowidr0   IntegrityErrorfetchoneint_extract_entities_resolve_entity_link_fact_entity_compute_hrr_vector_rebuild_bank)	r:   rI   rJ   rK   currM   rG   name	entity_ids	            r   add_factzMemoryStore.add_fact   sB    ZZ 	mmoG !<==+jj(( hd.@.@A 

!!#"}} ..w7 ; 006	&&w	:;
 $$Wg6x(?	 	 )) +jj((AG:(*  3y>**)	 	+	 	s1   D>AC#<AD>#AD;/D>:D;;D>>Equery	min_trustlimitc                    | j                   5  |j                         }|sg cddd       S ddlm} |j	                  |      }||g}d}|d}|j                  |       |j                  |       d| d}	| j                  j                  |	|      j                         }
|
D cg c]  }| j                  |       }}|ri|D cg c]  }|d   	 }}d	j                  d
t        |      z        }| j                  j                  d| d|       | j                  j                          |cddd       S c c}w c c}w # 1 sw Y   yxY w)zFull-text search over facts using FTS5.

        Returns a list of fact dicts ordered by FTS5 rank, then trust_score
        descending. Also increments retrieval_count for matched facts.
        Nr   )FactRetriever zAND f.category = ?az  
                SELECT f.fact_id, f.content, f.category, f.tags,
                       f.trust_score, f.retrieval_count, f.helpful_count,
                       f.created_at, f.updated_at
                FROM facts f
                JOIN facts_fts fts ON fts.rowid = f.fact_id
                WHERE facts_fts MATCH ?
                  AND f.trust_score >= ?
                  z[
                ORDER BY fts.rank, f.trust_score DESC
                LIMIT ?
            rM   ,?zIUPDATE facts SET retrieval_count = retrieval_count + 1 WHERE fact_id IN ())r8   rN   $plugins.memory.holographic.retrievalra   _sanitize_fts_queryappendr7   rD   rE   _row_to_dictjoinlenrF   )r:   r]   rJ   r^   r_   ra   match_queryparamscategory_clausesqlrowsrresultsidsplaceholderss                  r   search_factszMemoryStore.search_facts   sV    ZZ ,	KKME,	 ,	 K';;EBK'3F O#"6h'MM%  ## $C ::%%c62;;=D59:t((+:G:-45q|55"xxc#h7

""_`l_mmno 

!!#Y,	 ,	B ; 6I,	 ,	s0   D<A7D<"D2:D<D7AD<2
D<<ErM   trust_deltac                 d   | j                   5  | j                  j                  d|f      j                         }|
	 ddd       ydg}g }|0|j	                  d       |j	                  |j                                |"|j	                  d       |j	                  |       |"|j	                  d       |j	                  |       |3t        |d   |z         }	|j	                  d	       |j	                  |	       |j	                  |       | j                  j                  d
dj                  |       d|       | j                  j                          |p| j                  j                  d|f       | j                  |      D ]%  }
| j                  |
      }| j                  ||       ' | j                  j                          || j                  ||       |xs. | j                  j                  d|f      j                         d   }| j                  |       	 ddd       y# 1 sw Y   yxY w)zxPartially update a fact. Trust is clamped to [0, 1].

        Returns True if the row existed, False otherwise.
        z8SELECT fact_id, trust_score FROM facts WHERE fact_id = ?NFzupdated_at = CURRENT_TIMESTAMPzcontent = ?ztags = ?zcategory = ?trust_scoreztrust_score = ?zUPDATE facts SET z, z WHERE fact_id = ?+DELETE FROM fact_entities WHERE fact_id = ?z,SELECT category FROM facts WHERE fact_id = ?rJ   T)r8   r7   rD   rR   rh   rN   r   rj   rF   rT   rU   rV   rW   rX   )r:   rM   rI   rv   rK   rJ   rG   assignmentsrm   	new_trustrZ   r[   cats                r   update_factzMemoryStore.update_fact#  s    ZZ 2	**$$JWJhj  {2	 2	 'G%GKF"""=1gmmo."":.d##"">2h'&(]);k)IJ	""#45i(MM'"JJ#DIIk$:#;;MN JJ "

""AG: !227; ?D $ 4 4T :I**7I>? 

!!# "((': %djj00>
hj%C s#e2	 2	 2	s   /H&GH&&H/c                    | j                   5  | j                  j                  d|f      j                         }|
	 ddd       y| j                  j                  d|f       | j                  j                  d|f       | j                  j	                          | j                  |d          	 ddd       y# 1 sw Y   yxY w)zDDelete a fact and its entity links. Returns True if the row existed.z5SELECT fact_id, category FROM facts WHERE fact_id = ?NFry   z#DELETE FROM facts WHERE fact_id = ?rJ   T)r8   r7   rD   rR   rF   rX   )r:   rM   rG   s      r   remove_factzMemoryStore.remove_factc  s    ZZ 	**$$G'hj  {	 	 JJ=z JJDwjQJJs:/	 	 	s   /B7A)B77C c                 D   | j                   5  |g}d}|d}|j                  |       |j                  |       d| d}| j                  j                  ||      j	                         }|D cg c]  }| j                  |       c}cddd       S c c}w # 1 sw Y   yxY w)zxBrowse facts ordered by trust_score descending.

        Optionally filter by category and minimum trust score.
        rb   NzAND category = ?z
                SELECT fact_id, content, category, tags, trust_score,
                       retrieval_count, helpful_count, created_at, updated_at
                FROM facts
                WHERE trust_score >= ?
                  zO
                ORDER BY trust_score DESC
                LIMIT ?
            )r8   rh   r7   rD   rE   ri   )	r:   rJ   r^   r_   rm   rn   ro   rp   rq   s	            r   
list_factszMemoryStore.list_factst  s     ZZ 	8%;F O#"4h'MM% 
 ## $C ::%%c62;;=D267QD%%a(7%	8 	8$ 8%	8 	8s   A B-BBBBhelpfulc                    | j                   5  | j                  j                  d|f      j                         }|t	        d| d      |d   }|rt
        nt        }t        ||z         }|rdnd}| j                  j                  d|||f       | j                  j                          ||||d	   |z   d
cddd       S # 1 sw Y   yxY w)a  Record user feedback and adjust trust asymmetrically.

        helpful=True  -> trust += 0.05, helpful_count += 1
        helpful=False -> trust -= 0.10

        Returns a dict with fact_id, old_trust, new_trust, helpful_count.
        Raises KeyError if fact_id does not exist.
        zGSELECT fact_id, trust_score, helpful_count FROM facts WHERE fact_id = ?Nzfact_id z
 not foundrx   r   r   z
                UPDATE facts
                SET trust_score    = ?,
                    helpful_count  = helpful_count + ?,
                    updated_at     = CURRENT_TIMESTAMP
                WHERE fact_id = ?
                helpful_count)rM   	old_trustr{   r   )	r8   r7   rD   rR   KeyError_HELPFUL_DELTA_UNHELPFUL_DELTAr   rF   )r:   rM   r   rG   r   deltar{   helpful_increments           r   record_feedbackzMemoryStore.record_feedback  s     ZZ 	**$$Y
 hj  {'*=>>"=1I&-N3CE$Y%67I%,!JJ -w7	 JJ !( ) )!$_!58I!I	3	 	 	s   B)C  C	textc                    t               g dt        ddffd}t        j                  |      D ]  } ||j	                  d              t
        j                  |      D ]  } ||j	                  d              t        j                  |      D ]  } ||j	                  d              t        j                  |      D ]0  } ||j	                  d              ||j	                  d             2 S )a  Extract entity candidates from text using simple regex rules.

        Rules applied (in order):
        1. Capitalized multi-word phrases  e.g. "John Doe"
        2. Double-quoted terms             e.g. "Python"
        3. Single-quoted terms             e.g. 'pytest'
        4. AKA patterns                    e.g. "Guido aka BDFL" -> two entities

        Returns a deduplicated list preserving first-seen order.
        rZ   r   Nc                     | j                         }|rD|j                         vr1j                  |j                                j                  |       y y y r
   )rN   loweraddrh   )rZ   stripped
candidatesseens     r   _addz+MemoryStore._extract_entities.<locals>._add  sG    zz|HHNN,D8)*!!(+ 9xr   r      )setr$   _RE_CAPITALIZEDfinditergroup_RE_DOUBLE_QUOTE_RE_SINGLE_QUOTE_RE_AKA)r:   r   r   mr   r   s       @@r   rT   zMemoryStore._extract_entities  s      "
	,s 	,t 	, !))$/ 	A	 "**40 	A	 "**40 	A	 !!$' 	A	 r   rZ   c                    | j                   j                  d|f      j                         }|t        |d         S | j                   j                  d|f      j                         }|t        |d         S | j                   j                  d|f      }| j                   j	                          t        |j
                        S )zsFind an existing entity by name or alias (case-insensitive) or create one.

        Returns the entity_id.
        z0SELECT entity_id FROM entities WHERE name LIKE ?r[   zw
            SELECT entity_id FROM entities
            WHERE ',' || aliases || ',' LIKE '%,' || ? || ',%'
            z&INSERT INTO entities (name) VALUES (?))r7   rD   rR   rS   rF   rP   )r:   rZ   rG   	alias_rowrY   s        r   rU   zMemoryStore._resolve_entity  s     jj  >

(* 	 ?s;'(( JJ&& G
 (* 	  y-.. jj  4tg
 	

3==!!r   r[   c                 t    | j                   j                  d||f       | j                   j                          y)zFInsert into fact_entities, silently ignore if the link already exists.zl
            INSERT OR IGNORE INTO fact_entities (fact_id, entity_id)
            VALUES (?, ?)
            N)r7   rD   rF   )r:   rM   r[   s      r   rV   zMemoryStore._link_fact_entity   s5    

 i 	
 	

r   c                    | j                   5  | j                  s
	 ddd       y| j                  j                  d|f      j	                         }|D cg c]  }|d   	 }}t        j                  ||| j                        }| j                  j                  dt        j                  |      |f       | j                  j                          ddd       yc c}w # 1 sw Y   yxY w)zDCompute and store HRR vector for a fact. No-op if numpy unavailable.Nz
                SELECT e.name FROM entities e
                JOIN fact_entities fe ON fe.entity_id = e.entity_id
                WHERE fe.fact_id = ?
                rZ   z1UPDATE facts SET hrr_vector = ? WHERE fact_id = ?)
r8   r*   r7   rD   rE   r(   encode_factr   phases_to_bytesrF   )r:   rM   rI   rp   rG   entitiesvectors          r   rW   zMemoryStore._compute_hrr_vector  s    ZZ 	 &&	  	 
 ::%%
 
 hj  044F4H4__WhEFJJC$$V,g6 JJ)	  	  5	  	 s#   C/CCA.CCC$c                    | j                   5  | j                  s
	 ddd       yd| }| j                  j                  d|f      j	                         }|sA| j                  j                  d|f       | j                  j                          	 ddd       y|D cg c]  }t        j                  |d          }}t        j                  | }t        |      }t        j                  | j                  |       | j                  j                  d|t        j                  |      | j                  |f       | j                  j                          ddd       yc c}w # 1 sw Y   yxY w)zCFull rebuild of a category's memory bank from all its fact vectors.Nzcat:zJSELECT hrr_vector FROM facts WHERE category = ? AND hrr_vector IS NOT NULLz,DELETE FROM memory_banks WHERE bank_name = ?r@   a  
                INSERT INTO memory_banks (bank_name, vector, dim, fact_count, updated_at)
                VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)
                ON CONFLICT(bank_name) DO UPDATE SET
                    vector = excluded.vector,
                    dim = excluded.dim,
                    fact_count = excluded.fact_count,
                    updated_at = excluded.updated_at
                )r8   r*   r7   rD   rE   rF   r(   bytes_to_phasesbundlerk   snr_estimater   r   )r:   rJ   	bank_namerp   rG   vectorsbank_vector
fact_counts           r   rX   zMemoryStore._rebuild_bank#  sK   ZZ "	 &&"	  "	  xj)I::%%\ hj 
 

""#QT]S_`

!!#"	  "	  JNN#s**3|+<=NGN**g.KWJ T\\:6JJ C//<dllJW JJE"	  "	  O"	  "	 s*   EA*EEE:BEEE'dimc                    | j                   5  | j                  s
	 ddd       y||| _        | j                  j	                  d      j                         }t               }|D ].  }| j                  |d   |d          |j                  |d          0 |D ]  }| j                  |        t        |      cddd       S # 1 sw Y   yxY w)z}Recompute all HRR vectors + banks from text. For recovery/migration.

        Returns the number of facts processed.
        Nr   z,SELECT fact_id, content, category FROM factsrM   rI   rJ   )r8   r*   r   r7   rD   rE   r   rW   r   rX   rk   )r:   r   rp   
categoriesrG   rJ   s         r   rebuild_all_vectorszMemoryStore.rebuild_all_vectorsI  s    
 ZZ 	&&	 	 "::%%>hj  $'5J 0((YYHs:/0 ' -""8,- t9'	 	 	s   B?BB??CrG   c                     t        |      S )z&Convert a sqlite3.Row to a plain dict.)dict)r:   rG   s     r   ri   zMemoryStore._row_to_dictg  s    Cyr   c                    t        | dd      yt        j                  5  | j                  }|
	 ddd       y|dxx   dz  cc<   |d   dk  r>	 |d   j	                          t        j
                  j                  | j                  d       d| _        ddd       y# t        j
                  j                  | j                  d       w xY w# 1 sw Y   yxY w)a#  Release this instance's reference to the shared connection.

        The underlying connection is closed only when the last MemoryStore
        referencing the same database is closed, so closing one instance can
        never break sibling instances that still hold it. Idempotent.
        r6   Nr!   r   r   r   )getattrr   r.   r6   closer   popr,   )r:   r;   s     r   r   zMemoryStore.closek  s     44(0&& 
	KKE}
	 
	 &MQMV}!=&M'')''++DIIt<DK
	 
	  ''++DIIt<
	 
	s(   C
C
B!1C
,CC

Cc                     | S r
    )r:   s    r   	__enter__zMemoryStore.__enter__  s    r   _c                 $    | j                          y r
   )r   )r:   r   s     r   __exit__zMemoryStore.__exit__  s    

r   )Ng      ?i   )r   N)generalrb   )Ng333333?
   )NNNN)Nr   2   r
   )r   r   )$__name__
__module____qualname____doc__r   r   __annotations__r4   Lockr.   floatrS   r<   r9   r$   r\   listru   boolr}   r   r   r   rT   rU   rV   rW   rX   r   r0   r2   ri   r   r   objectr   r   r   r   r   r   b   s`   L GT"INN$M (,"	0,$0, 0, 	0,
 
0,l* "	++ + 	+
 
+`  $88 *8 	8
 8 
d8z #$(#>> t> T\	>
 Dj> *> 
>@3 4 &  $	8*8 8 	8
 
d8<'s 'T 'd 'Z!c !d3i !F"C "C "<	 	 	 	 3      0$ c $ d $ LsTz S <  *6 d r   r   )r   rer0   r4   pathlibr   rb   r   r(   ImportErrorrC   r   r   r   r   compiler   r   r   
IGNORECASEr   r   r   r   r   r   r   <module>r      s   
 
   $<~  

 2::EF2::l+ 2::l+ 2::BMM3 3% 3b bk  s   B 	BB