
    iO                     &   d Z ddlZddlZddlZddlmZ 	 ddlmZ n# e	$ r ddlZY nw xY w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dS )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                 R    t          t          t          t          |                     S N)max
_TRUST_MINmin
_TRUST_MAX)r   s    E/home/piyush/.hermes/hermes-agent/plugins/memory/holographic/store.py_clamp_trustr   ^   s    z3z511222    c                      e Zd ZdZ	 	 	 d1dddeded	dfd
Zd2dZ	 	 d3dededed	efdZ		 	 	 d4dededz  deded	e
e         f
dZ	 	 	 	 d5d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	 	 	 d6d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d7d)edz  d	efd*Zd+ej        d	efd,Zd2d-Zd8d.Zd/ed	dfd0ZdS )9MemoryStorezBSQLite-backed fact store with entity resolution and trust scoring.N      ?   db_pathzstr | Path | Nonedefault_trusthrr_dimr   c                    | ddl m} t           |            dz            }t          |                                          | _        | j        j                            dd           t          |          | _	        || _
        t          j        | _        t          j        t          | j                  dd          | _        t#          j                    | _        t          j        | j        _        |                                  d S )	Nr   )get_hermes_homezmemory_store.dbT)parentsexist_okFg      $@)check_same_threadtimeout)hermes_constantsr   strr   
expanduserr   parentmkdirr   r   r   hrr
_HAS_NUMPY_hrr_availablesqlite3connect_conn	threadingRLock_lockRowrow_factory_init_db)selfr   r   r   r   s        r   __init__zMemoryStore.__init__e   s     ?888888//++.??@@GG}}//11!!$!>>>)-88!n)0#*
 *
 *


 _&&
!(
r   c                 R   | j                             d           | j                             t                     d | j                             d                                          D             }d|vr| j                             d           | j                                          dS )zKCreate tables, indexes, and triggers if they do not exist. Enable WAL mode.zPRAGMA journal_mode=WALc                     h | ]
}|d          S )r    .0rows     r   	<setcomp>z'MemoryStore._init_db.<locals>.<setcomp>   s    ___c3q6___r   zPRAGMA table_info(facts)
hrr_vectorz,ALTER TABLE facts ADD COLUMN hrr_vector BLOBN)r)   executeexecutescript_SCHEMAfetchallcommit)r0   columnss     r   r/   zMemoryStore._init_db   s    
4555
  )))__TZ%7%78R%S%S%\%\%^%^___w&&JMNNN
r   general contentcategorytagsc                    | j         5  |                                }|st          d          	 | j                            d|||| j        f          }| j                                         |j        }nd# t          j	        $ rR | j                            d|f          
                                }t          |d                   cY cddd           S w xY w|                     |          D ]-}|                     |          }|                     ||           .|                     ||           |                     |           |cddd           S # 1 swxY w Y   dS )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)r,   strip
ValueErrorr)   r:   r   r>   	lastrowidr'   IntegrityErrorfetchoneint_extract_entities_resolve_entity_link_fact_entity_compute_hrr_vector_rebuild_bank)	r0   rB   rC   rD   currF   r7   name	entity_ids	            r   add_factzMemoryStore.add_fact   s    Z 	 	mmooG > !<===+j(( hd.@A  
!!###"}) + + +j((AG: (**  3y>****)	 	 	 	 	 	 	 	+ ..w77 ; ; 0066	&&w	:::: $$Wg666x(((?	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s7   &EAA43E4ACECA2EEE333333?
   query	min_trustlimitc                 R     j         5  |                                }|sg cddd           S ||g}d}|d}|                    |           |                    |           d| d} j                            ||                                          } fd|D             }	|	rid |	D             }
d                    d	t          |
          z            } j                            d
| d|
            j                                         |	cddd           S # 1 swxY w Y   dS )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.
        NrA   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 ?
            c                 :    g | ]}                     |          S r4   _row_to_dictr6   rr0   s     r   
<listcomp>z,MemoryStore.search_facts.<locals>.<listcomp>   s'    :::t((++:::r   c                     g | ]
}|d          S )rF   r4   )r6   r`   s     r   ra   z,MemoryStore.search_facts.<locals>.<listcomp>   s    555q|555r   ,?zIUPDATE facts SET retrieval_count = retrieval_count + 1 WHERE fact_id IN ())	r,   rG   appendr)   r:   r=   joinlenr>   )r0   rX   rC   rY   rZ   paramscategory_clausesqlrowsresultsidsplaceholderss   `           r   search_factszMemoryStore.search_facts   s    Z %	 %	KKMME %	 %	 %	 %	 %	 %	 %	 %	
 "9-F O#"6h'''MM%    #  C :%%c622;;==D::::T:::G $55W555"xxc#hh77
""o`looo   
!!###K%	 %	 %	 %	 %	 %	 %	 %	 %	 %	 %	 %	 %	 %	 %	 %	 %	 %	s   DC!DD #D rF   trust_deltac                    | j         5  | j                            d|f                                          }|	 ddd           dS dg}g }|<|                    d           |                    |                                           |*|                    d           |                    |           |*|                    d           |                    |           |Bt          |d         |z             }	|                    d	           |                    |	           |                    |           | j                            d
d                    |           d|           | j                                         |x| j                            d|f           | 	                    |          D ]-}
| 
                    |
          }|                     ||           .| j                                         ||                     ||           |p3| j                            d|f                                          d         }|                     |           	 ddd           dS # 1 swxY w Y   dS )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 = ?rC   T)r,   r)   r:   rK   rf   rG   r   rg   r>   rM   rN   rO   rP   rQ   )r0   rF   rB   rq   rD   rC   r7   assignmentsri   	new_trustrS   rT   cats                r   update_factzMemoryStore.update_fact   s     Z 2	 2	*$$JWJ hjj  {2	 2	 2	 2	 2	 2	 2	 2	 'G%GKF"""=111gmmoo..."":...d####"">222h'''&(]);k)IJJ	""#4555i(((MM'"""JNDIIk$:$:NNN   J "
""AG:   !227;; ? ?D $ 4 4T : :I**7I>>>>
!!### "(('::: %dj00>
 hjj%C s###e2	 2	 2	 2	 2	 2	 2	 2	 2	 2	 2	 2	 2	 2	 2	 2	 2	 2	s   2I1HI11I58I5c                    | j         5  | j                            d|f                                          }|	 ddd           dS | j                            d|f           | j                            d|f           | j                                         |                     |d                    	 ddd           dS # 1 swxY w Y   dS )zDDelete a fact and its entity links. Returns True if the row existed.z5SELECT fact_id, category FROM facts WHERE fact_id = ?NFrt   z#DELETE FROM facts WHERE fact_id = ?rC   T)r,   r)   r:   rK   r>   rQ   )r0   rF   r7   s      r   remove_factzMemoryStore.remove_fact.  s9   Z 	 	*$$G' hjj  {	 	 	 	 	 	 	 	 J=z   JDwjQQQJs:///	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s   2CA-CCCr   2   c                 .     j         5  |g}d}|d}|                    |           |                    |           d| d} j                            ||                                          } fd|D             cddd           S # 1 swxY w Y   dS )zxBrowse facts ordered by trust_score descending.

        Optionally filter by category and minimum trust score.
        rA   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 ?
            c                 :    g | ]}                     |          S r4   r]   r_   s     r   ra   z*MemoryStore.list_facts.<locals>.<listcomp>[  s'    777QD%%a((777r   )r,   rf   r)   r:   r=   )r0   rC   rY   rZ   ri   rj   rk   rl   s   `       r   
list_factszMemoryStore.list_facts?  s    Z 	8 	8%;F O#"4h'''MM%   
 #  C :%%c622;;==D7777$777%	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8s   A4B

BBhelpfulc                    | j         5  | j                            d|f                                          }|t	          d| d          |d         }|rt
          nt          }t          ||z             }|rdnd}| j                            d|||f           | j                                         ||||d	         |z   d
cddd           S # 1 swxY w Y   dS )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 foundrs   r   r   z
                UPDATE facts
                SET trust_score    = ?,
                    helpful_count  = helpful_count + ?,
                    updated_at     = CURRENT_TIMESTAMP
                WHERE fact_id = ?
                helpful_count)rF   	old_trustrv   r   )	r,   r)   r:   rK   KeyError_HELPFUL_DELTA_UNHELPFUL_DELTAr   r>   )r0   rF   r   r7   r   deltarv   helpful_increments           r   record_feedbackzMemoryStore.record_feedback]  s]    Z 	 	*$$Y
  hjj  {='===>>>"=1I&-CNN3CE$Y%677I%, 3!J -w7	 	 	 J !( ) )!$_!58I!I	 3	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s   B:CCCtextc                 ^   t                      g dt          ddffd}t                              |          D ] } ||                    d                     !t
                              |          D ] } ||                    d                     !t                              |          D ] } ||                    d                     !t                              |          D ]>} ||                    d                      ||                    d                     ?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.
        rS   r   Nc                     |                                  }|rT|                                vr@                    |                                                               |           d S d S d S r
   )rG   loweraddrf   )rS   stripped
candidatesseens     r   _addz+MemoryStore._extract_entities.<locals>._add  sr    zz||H ,HNN,,D88))***!!(+++++, ,88r   r      )setr    _RE_CAPITALIZEDfinditergroup_RE_DOUBLE_QUOTE_RE_SINGLE_QUOTE_RE_AKA)r0   r   r   mr   r   s       @@r   rM   zMemoryStore._extract_entities  sO     "
	,s 	,t 	, 	, 	, 	, 	, 	, 	, !))$// 	 	AD!**400 	 	AD!**400 	 	AD!!$'' 	 	ADDr   rS   c                    | j                             d|f                                          }|t          |d                   S | j                             d|f                                          }|t          |d                   S | j                             d|f          }| 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 ?NrT   zw
            SELECT entity_id FROM entities
            WHERE ',' || aliases || ',' LIKE '%,' || ? || ',%'
            z&INSERT INTO entities (name) VALUES (?))r)   r:   rK   rL   r>   rI   )r0   rS   r7   	alias_rowrR   s        r   rN   zMemoryStore._resolve_entity  s     j  >
 

(** 	 ?s;'((( J&& G
 
 (** 	  y-... j  4tg
 
 	
3=!!!r   rT   c                 r    | j                             d||f           | j                                          dS )zFInsert into fact_entities, silently ignore if the link already exists.zl
            INSERT OR IGNORE INTO fact_entities (fact_id, entity_id)
            VALUES (?, ?)
            N)r)   r:   r>   )r0   rF   rT   s      r   rO   zMemoryStore._link_fact_entity  sG    
 i 	
 	
 	
 	
r   c                    | j         5  | j        s	 ddd           dS | j                            d|f                                          }d |D             }t          j        ||| j                  }| j                            dt          j        |          |f           | j        	                                 ddd           dS # 1 swxY w Y   dS )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 = ?
                c                     g | ]
}|d          S )rS   r4   r5   s     r   ra   z3MemoryStore._compute_hrr_vector.<locals>.<listcomp>  s    444F444r   z1UPDATE facts SET hrr_vector = ? WHERE fact_id = ?)
r,   r&   r)   r:   r=   r$   encode_factr   phases_to_bytesr>   )r0   rF   rB   rl   entitiesvectors         r   rP   zMemoryStore._compute_hrr_vector  sG   Z 	  	 & 	  	  	  	  	  	  	  	 
 :%%
 
  hjj  54t444H_WhEEFJC$V,,g6   J)	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	 s   	CBCCCc                    | j         5  | j        s	 ddd           dS d| }| j                            d|f                                          }|sC| j                            d|f           | j                                         	 ddd           dS d |D             }t          j        | }t          |          }t          j	        | j
        |           | j                            d|t          j        |          | j
        |f           | j                                         ddd           dS # 1 swxY w Y   dS )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 = ?c                 B    g | ]}t          j        |d                    S )r9   )r$   bytes_to_phasesr5   s     r   ra   z-MemoryStore._rebuild_bank.<locals>.<listcomp>  s(    NNN#s*3|+<==NNNr   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
                )r,   r&   r)   r:   r=   r>   r$   bundlerh   snr_estimater   r   )r0   rC   	bank_namerl   vectorsbank_vector
fact_counts          r   rQ   zMemoryStore._rebuild_bank  s   Z "	  "	 & "	  "	  "	  "	  "	  "	  "	  "	  *x))I:%%\  hjj 
  
""#QT]S_```
!!###"	  "	  "	  "	  "	  "	  "	  "	  ONNNNG*g.KWJ T\:666J C/<<dlJW   JE"	  "	  "	  "	  "	  "	  "	  "	  "	  "	  "	  "	  "	  "	  "	  "	  "	  "	 s   	D5A+D5BD55D9<D9dimc                    | j         5  | j        s	 ddd           dS ||| _        | j                            d                                          }t                      }|D ]?}|                     |d         |d                    |                    |d                    @|D ]}| 	                    |           t          |          cddd           S # 1 swxY w Y   dS )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 factsrF   rB   rC   )r,   r&   r   r)   r:   r=   r   rP   r   rQ   rh   )r0   r   rl   
categoriesr7   rC   s         r   rebuild_all_vectorszMemoryStore.rebuild_all_vectors  sZ   
 Z 	 	& 	 	 	 	 	 	 	 	 ":%%> hjj  $'55J 0 0((YYHHHs:////& - -""8,,,,t99'	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s   	CB-CCCr7   c                      t          |          S )z&Convert a sqlite3.Row to a plain dict.)dict)r0   r7   s     r   r^   zMemoryStore._row_to_dict2  s    Cyyr   c                 8    | j                                          dS )zClose the database connection.N)r)   closer0   s    r   r   zMemoryStore.close6  s    
r   c                     | S r
   r4   r   s    r   	__enter__zMemoryStore.__enter__:  s    r   _c                 .    |                                   d S r
   )r   )r0   r   s     r   __exit__zMemoryStore.__exit__=  s    

r   )Nr   r   )r   N)r@   rA   )NrV   rW   )NNNN)Nr   r{   r
   )r   r   )__name__
__module____qualname____doc__floatrL   r1   r/   r    rU   listr   rp   boolrx   rz   r~   r   rM   rN   rO   rP   rQ   r   r'   r-   r^   r   r   objectr   r4   r   r   r   r   b   s       LL (,"	 $  	
 
   6   " "	+ ++ + 	+
 
+ + + +`  $1 11 *1 	1
 1 
d1 1 1 1l #$(#> >> t> T\	>
 Dj> *> 
> > > >@3 4    &  $	8 8*8 8 	8
 
d8 8 8 8<'s 'T 'd ' ' ' 'Z!c !d3i ! ! ! !F"C "C " " " "<	 	 	 	 	 	 	 3            0$ c $ d $  $  $  $ L sTz S    <           6 d      r   r   )r   rer'   r*   pathlibr   rA   r   r$   ImportErrorr<   r   r   r   r   compiler   r   r   
IGNORECASEr   r   r   r   r4   r   r   <module>r      sX   
 
			           $$$$$$$   <~  

 2:EFF2:l++ 2:l++ 2:BM 3 3% 3 3 3 3\ \ \ \ \ \ \ \ \ \s    	))