
    i                        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
 ddlmZ ddlmZmZmZ  ej"                  e      Zd	Zd
ZdZddZ G d d      Zy)u)  
Qdrant Retriever — RLM Nervous System Story 2.2
================================================
Wraps core/kb/qdrant_store.py and core/kb/embedder.py to expose
IRetriever-compatible semantic search over the genesis_memories collection.

PRD: _bmad-output/RLM_NERVOUS_SYSTEM_PRD.md (Story 2.2)
    )annotationsN)datetimetimezone)ListOptional)
embed_text)_get_client)
IRetrieverRetrievalRequestRetrievedChunkgenesis_memories)contenttext
created_atc                   | j                  t              }|sy	 t        |t        t        f      r/t        j                  t	        |      t        j                        }n.t        j                  t        |      j                  dd            }t        j                  t        j                        |z
  }t        d|j                        S # t        $ r Y yw xY w)z
    Compute how many days old this point is.

    Reads payload['created_at'] (ISO-8601 string or Unix epoch float).
    Returns 0 if the field is absent or unparseable.
    r   )tzZz+00:00)get_CREATED_AT_FIELD
isinstanceintfloatr   fromtimestampr   utcfromisoformatstrreplacenowmaxdays	Exception)payloadrawcreateddeltas       =/mnt/e/genesis-system/core/nervous_system/qdrant_retriever.py_freshness_daysr'      s     ++'
(CcC<(,,U3ZHLLIG,,SX-=-=c8-LMG-71ejj!! s   B/C
 
	CCc                  >    e Zd ZdZefddZddZd	dZed
d       Z	y)QdrantRetrievera]  
    IRetriever implementation backed by Qdrant's genesis_memories collection.

    Lifecycle:
        r = QdrantRetriever()          # collection defaults to genesis_memories
        r.health_check()               # True if collection reachable with >0 points
        chunks = r.retrieve(request)   # semantic search, filtered by min_relevance
    c                0    || _         t               | _        y N)_collectionr	   _client)self
collections     r&   __init__zQdrantRetriever.__init__<   s    %"}    c                j   |j                   dd }	 t        |      }	 | j
                  j                  | j                  ||j                  d      }t        |d      r|j                  n
t        |      }g }|D ]  }t        |dd	      }	|	|j                  k  r |j                  xs i }
d
}t        D ]"  }|
j!                  |      }|st#        |      } n |
j%                         D ci c]  \  }}|t        vr|| }}}|j'                  t)        || j*                  t-        |	d      t/        |
      |              |j1                  d d       |S # t        $ r"}t        j	                  d|       g cY d}~S d}~ww xY w# t        $ r"}t        j	                  d|       g cY d}~S d}~ww xY wc c}}w )a  
        Embed query text and search Qdrant for the top_k most similar points.

        Filters out any points whose score is below request.min_relevance.
        Results are returned sorted descending by relevance_score.

        BB test targets:
            - Returns non-empty list for a known query ("GHL integration")
            - All returned chunks have relevance_score >= request.min_relevance
            - Returns [] for an absurdly high min_relevance (e.g. 0.9999)
            - Truncates query at 512 chars before embedding

        WB test targets:
            - _freshness_days(payload) called and value stored in freshness_days
            - metadata dict excludes content/text keys
        Ni   z/QdrantRetriever.retrieve: embed_text failed: %sT)collection_namequerylimitwith_payloadz1QdrantRetriever.retrieve: Qdrant query failed: %spointsscoreg            )r   sourcerelevance_scorefreshness_daysmetadatac                    | j                   S r+   )r<   )cs    r&   <lambda>z*QdrantRetriever.retrieve.<locals>.<lambda>   s    !"3"3 r1   )keyreverse)r4   r   r!   loggererrorr-   query_pointsr,   top_khasattrr7   listgetattrmin_relevancer"   _CONTENT_FIELDSr   r   itemsappendr   source_nameroundr'   sort)r.   request
query_textvecexcresponser7   chunkspointr8   r"   r   fieldvalkvr>   s                    r&   retrievezQdrantRetriever.retrieveB   s   " ]]4C(
	Z(C
		||00 $ 0 0mm!	 1 H %,Hh$?T(^') 	E"5'37Ew,,,!MM/RG G( kk%(!#hG	 ")AO+ 1H 
 MM#++$)%O#27#;%+	@ 	3TBi  	LLJCPI	  	LLLcRI	0s@   E 3F 'F/	E>E93E>9E>	F,
F'!F,'F,c                    	 | j                   j                  | j                        }|j                  }|duxr |dkD  S # t        $ r }t
        j                  d|       Y d}~yd}~ww xY w)z
        Return True if genesis_memories collection is reachable and non-empty.

        BB test: returns True against live Elestio Qdrant (28,435+ points).
        WB test: catches exception and returns False without raising.
        )r3   Nr   z'QdrantRetriever.health_check failed: %sF)r-   get_collectionr,   points_countr!   rD   warning)r.   infocountrU   s       r&   health_checkzQdrantRetriever.health_check   se    	<<..t?O?O.PD#'#4#4E$22 	NNDcJ	s   <? 	A(A##A(c                     d| j                    S )z:Canonical source identifier used in RetrievedChunk.source.zqdrant:)r,   )r.   s    r&   rO   zQdrantRetriever.source_name   s     ))*++r1   N)r/   r   returnNone)rR   r   rf   zList[RetrievedChunk])rf   bool)rf   r   )
__name__
__module____qualname____doc___DEFAULT_COLLECTIONr0   r]   rd   propertyrO    r1   r&   r)   r)   2   s1     *= %HT , ,r1   r)   )r"   dictrf   r   )rl   
__future__r   loggingr   r   typingr   r   core.kb.embedderr   core.kb.qdrant_storer	   core.nervous_system.contractsr
   r   r   	getLoggerri   rD   rm   rL   r   r'   r)   ro   r1   r&   <module>rx      sX    #  ' ! ' , V V			8	$( %  *l, l,r1   