
    $jK?                         d Z ddlZddlmZmZ ddlmZmZ ddlmZm	Z	m
Z
mZmZmZmZmZmZmZmZmZmZmZ  G d d          ZdS )	z
Search tracer for collecting detailed search execution traces.

The SearchTracer collects comprehensive information about each step
of the spreading activation search process for debugging and visualization.
    N)UTCdatetime)AnyLiteral   )
EntryPointLinkInfo	NodeVisitPruningDecision	QueryInfoRerankedResultRetrievalMethodResultsRetrievalResultRRFMergeResultSearchPhaseMetricsSearchSummarySearchTraceTemporalConstraintWeightComponentsc                   d   e Zd ZdZ	 	 d@dedededee         dz  dedz  f
dZd	 Zd
ee	         fdZ
dedz  dedz  fdZdedede	defdZdededededz  dededz  ded         dz  de	dz  de	de	de	de	de	fd Z	 	 dAd"ed#eded         de	d$edz  d%e	dz  d&ed'edz  d(efd)Zded*ed+         de	fd,ZdBd-ed.e	d/eeef         dz  fd0Z	 	 d@d1ed2         d3ee         d.e	d4ed5eeef         dz  d6edz  fd7Zd8ee         fd9Zd:eeeef                  d;efd<Zd=eeeef                  d>efd?ZdS )CSearchTracera  
    Tracer for collecting detailed search execution information.

    Usage:
        tracer = SearchTracer(query="Who is Alice?", budget=50, max_tokens=4096)
        tracer.start()

        # During search...
        tracer.record_query_embedding(embedding)
        tracer.add_entry_point(node_id, text, similarity, rank)
        tracer.visit_node(...)
        tracer.prune_node(...)

        # After search...
        trace = tracer.finalize(final_results)
        json_output = trace.to_json()
    Nquerybudget
max_tokenstags
tags_matchc                 6   || _         || _        || _        || _        || _        d| _        d| _        g | _        g | _        g | _	        g | _
        d| _        g | _        g | _        g | _        d| _        t!                      | _        d| _        d| _        d| _        dS )a?  
        Initialize tracer.

        Args:
            query: Search query text
            budget: Maximum nodes to explore
            max_tokens: Maximum tokens to return in results
            tags: Tags filter applied to recall
            tags_match: Tags matching mode (any, all, any_strict, all_strict)
        Nr   )
query_textr   r   r   r   query_embedding
start_timeentry_pointsvisitsprunedphase_metricstemporal_constraintretrieval_results
rrf_mergedrerankedcurrent_stepsetnodes_visited_settemporal_links_followedsemantic_links_followedentity_links_followed)selfr   r   r   r   r   s         j/home/rurouni/.hermes/hermes-agent/venv/lib/python3.11/site-packages/hindsight_api/engine/search/tracer.py__init__zSearchTracer.__init__1   s    $  $	$ 48(,.0')-/79 ?C  @B02.0 !$ ()$'($%&"""    c                 6    t          j                     | _        dS )zStart timing the search.N)timer    )r/   s    r0   startzSearchTracer.startb   s    )++r2   	embeddingc                     || _         dS )zRecord the query embedding.N)r   )r/   r6   s     r0   record_query_embeddingz#SearchTracer.record_query_embeddingf   s    (r2   r5   endc                 >    ||t          ||          | _        dS dS )z<Record the detected temporal constraint from query analysis.N)r5   r9   )r   r%   )r/   r5   r9   s      r0   record_temporal_constraintz'SearchTracer.record_temporal_constraintj   s.    '93'O'O'OD$$$ !0r2   node_idtext
similarityrankc                     t          dt          d|                    }| j                            t	          ||||                     dS )z
        Record an entry point.

        Args:
            node_id: Memory unit ID
            text: Memory unit text
            similarity: Cosine similarity to query
            rank: Rank among entry points (1-based)
              ?        )r<   r=   similarity_scorer?   N)minmaxr!   appendr   )r/   r<   r=   r>   r?   s        r0   add_entry_pointzSearchTracer.add_entry_pointo   s`     c#z2233
  !+	  	
 	
 	
 	
 	
r2   context
event_dateis_entry_pointparent_node_id	link_type)temporalsemanticentitylink_weight
activationsemantic_similarityrecency	frequencyfinal_weightc                 Z   | xj         dz  c_         | j                            |           t          dt	          d|
                    }
t          dt	          d|                    }t          dt	          d|                    }t          |	|
|||d|	z  d|
z  d|z  d|z  	  	        }t          | j         |||||||||g d	          }| j                            |           |d
k    r| xj	        dz  c_	        dS |dk    r| xj
        dz  c_
        dS |dk    r| xj        dz  c_        dS dS )a  
        Record visiting a node.

        Args:
            node_id: Memory unit ID
            text: Memory unit text
            context: Memory unit context
            event_date: When the memory occurred
            is_entry_point: Whether this is an entry point
            parent_node_id: Node that led here (None for entry points)
            link_type: Type of link from parent
            link_weight: Weight of link from parent
            activation: Activation score
            semantic_similarity: Semantic similarity to query
            recency: Recency weight
            frequency: Frequency weight
            final_weight: Combined final weight
        r   rA   rB   g333333?g      ?g333333?)	rQ   rR   rS   rT   rU   activation_contributionsemantic_contributionrecency_contributionfrequency_contributionN)stepr<   r=   rH   rI   rJ   rK   rL   rP   weightsneighbors_explored
final_rankrM   rN   rO   )r)   r+   addrD   rE   r   r
   r"   rF   r,   r-   r.   )r/   r<   r=   rH   rI   rJ   rK   rL   rP   rQ   rR   rS   rT   rU   r\   visits                   r0   
visit_nodezSearchTracer.visit_node   s   D 	Q""7+++ "#s30C'D'DEEc3sG,,--Si0011	 #! 3%$'*$4"%(;";!%#')#3

 

 

 "!))#!
 
 
 	5!!! 
""((A-((((*$$((A-(((((""&&!+&&&& #"r2   Ffrom_node_id
to_node_id	entity_idnew_activationfollowedprune_reasonis_supplementaryc
           
          d}
| j         D ]}|j        |k    r|}
 n|
dS t          ||||||||	          }|
j                            |           dS )aO  
        Record a link to a neighbor (whether followed or not).

        Args:
            from_node_id: Source node
            to_node_id: Target node
            link_type: Type of link
            link_weight: Weight of link
            entity_id: Entity ID if link is entity-based
            new_activation: Activation passed to neighbor (None for supplementary links)
            followed: Whether link was followed
            prune_reason: Why link was not followed (if not followed)
            is_supplementary: Whether this is a supplementary link (multiple connections)
        N)rc   rL   rP   rd   re   rf   rg   rh   )r"   r<   r	   r]   rF   )r/   rb   rc   rL   rP   rd   re   rf   rg   rh   r`   v	link_infos                r0   add_neighbor_linkzSearchTracer.add_neighbor_link   s    6  	 	AyL(( ) =F!#)%-	
 	
 	
	 	 ''	22222r2   reason)already_visitedactivation_too_lowbudget_exhaustedc           	      l    | j                             t          |||| j        dz                        dS )z
        Record a node being pruned (not visited).

        Args:
            node_id: Node that was pruned
            reason: Why it was pruned
            activation: Activation value when pruned
        r   )r<   rm   rQ   would_have_been_stepN)r#   rF   r   r)   )r/   r<   rm   rQ   s       r0   
prune_nodezSearchTracer.prune_node  sN     	%%)%6%:	  	
 	
 	
 	
 	
r2   
phase_nameduration_secondsdetailsc                 ^    | j                             t          |||pi                      dS )z
        Record metrics for a search phase.

        Args:
            phase_name: Name of the phase
            duration_seconds: Time taken
            details: Additional phase-specific details
        )rt   ru   rv   N)r$   rF   r   )r/   rt   ru   rv   s       r0   add_phase_metriczSearchTracer.add_phase_metric  sH     	!!%!12  	
 	
 	
 	
 	
r2   method_name)rN   bm25graphrM   resultsscore_fieldmetadata	fact_typec                    g }t          |d          D ]\  }\  }	}
|
                    |          }|d}|                    t          ||	|
                    d          pd|
                    d          pd|
                    d          |
                    d	          p|||
                     | j                            t          |||||pi                      dS )a  
        Record results from a single retrieval method.

        Args:
            method_name: Name of the retrieval method
            results: List of (doc_id, data) tuples from retrieval
            duration_seconds: Time taken for this retrieval
            score_field: Field name containing the score in data dict
            metadata: Optional metadata about this retrieval method
            fact_type: Fact type this retrieval was for (world, experience)
        r   r5   NrB   r=    rH   rI   r   )r?   r<   r=   rH   rI   r   score
score_name)ry   r   r|   ru   r~   )	enumerategetrF   r   r&   r   )r/   ry   r|   ru   r}   r~   r   r&   r?   doc_iddatar   s               r0   add_retrieval_resultsz"SearchTracer.add_retrieval_results0  s   ( $-gQ$?$?$? 	 	 D.64HH[))E}$$"&))/R HHY//52#xx55"hh{33@y*	 	 	    	%%"'#)!1!R  	
 	
 	
 	
 	
r2   merged_resultsc                    g | _         t          |d          D ]q\  }\  }}}| j                             t          ||                    dd          |                    dd          |                    di           |                     rd	S )
z
        Record RRF merged results.

        Args:
            merged_results: List of (doc_id, data, rrf_meta) tuples from RRF merge
        r   r   r=   r   	rrf_scorerB   source_ranks)r<   r=   r   r   final_rrf_rankN)r'   r   rF   r   r   )r/   r   r?   r   r   rrf_metas         r0   add_rrf_mergedzSearchTracer.add_rrf_merged`  s     .7a.P.P.P 		 		*D*64O"""&"--&ll;<<!)nb!A!A#'     		 		r2   reranked_resultsr'   c                    i }| j         D ]}|j        ||j        <   g | _        t	          |d          D ]\  }}|d         }|                    |t          |          dz             }||z
  }	i }
dD ]}||v r||         ||         |
|<   | j                            t          ||                    dd          |                    dd	          |||	|

                     dS )z
        Record reranked results.

        Args:
            reranked_results: List of result dicts after reranking
            rrf_merged: Original RRF merged results for comparison
        r   r   id)cross_encoder_scorecross_encoder_score_normalizedr   rrf_normalizedrM   rS   combined_scoreNr=   r   weightrB   )r<   r=   rerank_scorererank_rankrrf_rankrank_changescore_components)	r'   r   r<   r(   r   r   lenrF   r   )r/   r   r'   rrf_rank_mapitemr?   resultr<   r   r   r   keys               r0   add_rerankedzSearchTracer.add_rerankeds  s/    O 	= 	=D)-)<L&&%&6a@@@ 	 	LD&TlG#''Z11DEEH"T/K
  " 
8 
8 &==VC[%<,23K$S)M  #FB//!'Hc!:!: $% +%5  
 
 
 
+	 	r2   final_resultsreturnc                 ,   | j         t          d          t          j                    | j         z
  }t          |d          D ]+\  }}|d         }| j        D ]}|j        |k    r	||_         n,t          | j        | j	        pg t          j        t                    | j        | j        | j        | j        | j                  }t%          t'          | j                  t'          | j                  t'          | j                  t'          | j                  | j        t'          | j                  z
  |t'          |          | j        | j        | j        | j                  }t5          || j        | j        | j        | j        | j        | j        ||	  	        }	|	S )z
        Finalize the trace and return the complete SearchTrace object.

        Args:
            final_results: Final ranked results returned to user

        Returns:
            Complete SearchTrace object
        Nz'Tracer not started - call start() firstr   r   )r   r   	timestampr   r   r   r   r%   )total_nodes_visitedtotal_nodes_prunedentry_points_foundbudget_usedbudget_remainingtotal_duration_secondsresults_returnedr,   r-   r.   r$   )	r   r&   r'   r(   r!   r"   r#   summaryr   )r    
ValueErrorr4   r   r"   r<   r^   r   r   r   r   nowr   r   r   r   r   r%   r   r   r#   r!   r,   r-   r.   r$   r   r&   r'   r(   )
r/   r   total_durationr?   r   result_node_idr`   
query_infor   traces
             r0   finalizezSearchTracer.finalize  s    ?"FGGGt6 &mQ77 	 	LD&#D\N  =N22'+E$E 3
  06Bl3''; $ 8	
 	
 	

   #DK 0 0"4;//"4#455DK((![3t{+;+;;#1 //$($@$($@"&"<,
 
 
 "4]*;;'

 

 

 r2   )NN)NF)N)__name__
__module____qualname____doc__strintlistr1   r5   floatr8   r   r;   rG   boolr   ra   rl   rs   dictr   rx   tupler   r   r   r   r    r2   r0   r   r      s        . "&!%/' /'/' /' 	/'
 3i$/' $J/' /' /' /'b& & &)U ) ) ) )P4 PhQUo P P P P

s 
# 
5 
PS 
 
 
 
,O,O, O, 	O,
 tOO, O, d
O, ;<tCO, T\O, O, #O, O, O, O, O, O, O,t $(!&03 0303 03 ;<	03
 03 :03 03 03 Dj03 03 03 03 03d

 ST
 	
 
 
 
.
 
3 
% 
RVWZ\_W_R`cgRg 
 
 
 
. +/ $.
 .
DE.
 e.
  	.

 .
 sCx.4'.
 :.
 .
 .
 .
`T%[    &-T$sCx.-A -t - - - -^?d4S>&: ?{ ? ? ? ? ? ?r2   r   )r   r4   r   r   typingr   r   r   r   r	   r
   r   r   r   r   r   r   r   r   r   r   r   r   r   r2   r0   <module>r      s&     " " " " " " " "                                       $C C C C C C C C C Cr2   