@@ -7,7 +7,7 @@ use super::async_trait;
77use std:: time:: Instant ;
88use tracing:: info;
99
10- use crate :: document:: { DocumentTree , NodeId , TocView } ;
10+ use crate :: document:: { DocumentTree , NodeId , RefType , ReferenceExtractor , TocView } ;
1111use crate :: error:: Result ;
1212
1313use super :: { AccessPattern , IndexStage , StageResult } ;
@@ -93,6 +93,46 @@ impl EnrichStage {
9393 }
9494 }
9595 }
96+
97+ /// Extract and resolve in-document cross-references for all nodes.
98+ ///
99+ /// Parses content for patterns like "see Section 2.1", "Appendix G", etc.
100+ /// and resolves them to actual `NodeId`s in the tree using the retrieval
101+ /// index for fast lookup.
102+ fn resolve_references ( tree : & mut DocumentTree ) -> usize {
103+ let retrieval_index = tree. build_retrieval_index ( ) ;
104+ let node_ids: Vec < NodeId > = tree. traverse ( ) . into_iter ( ) . collect ( ) ;
105+ let mut total_resolved = 0 ;
106+
107+ for node_id in node_ids {
108+ let content = tree. get ( node_id) . map ( |n| n. content . clone ( ) ) . unwrap_or_default ( ) ;
109+ if content. is_empty ( ) {
110+ continue ;
111+ }
112+
113+ // Quick check: skip nodes without any reference-like patterns
114+ let content_lower = content. to_lowercase ( ) ;
115+ let has_ref_pattern = content_lower. contains ( "section" )
116+ || content_lower. contains ( "appendix" )
117+ || content_lower. contains ( "table" )
118+ || content_lower. contains ( "figure" )
119+ || content_lower. contains ( "page" )
120+ || content_lower. contains ( "equation" ) ;
121+
122+ if !has_ref_pattern {
123+ continue ;
124+ }
125+
126+ let refs = ReferenceExtractor :: extract_and_resolve ( & content, tree, & retrieval_index) ;
127+ let resolved = refs. iter ( ) . filter ( |r| r. is_resolved ( ) ) . count ( ) ;
128+ if resolved > 0 {
129+ total_resolved += resolved;
130+ }
131+ tree. set_references ( node_id, refs) ;
132+ }
133+
134+ total_resolved
135+ }
96136}
97137
98138impl Default for EnrichStage {
@@ -142,7 +182,13 @@ impl IndexStage for EnrichStage {
142182 let ( total_tokens, node_count) = Self :: calculate_token_stats ( tree) ;
143183 info ! ( "Total tokens: {}, nodes: {}" , total_tokens, node_count) ;
144184
145- // 4. Generate document description
185+ // 4. Extract and resolve cross-references
186+ let resolved_refs = Self :: resolve_references ( tree) ;
187+ if resolved_refs > 0 {
188+ info ! ( "Resolved {} cross-references" , resolved_refs) ;
189+ }
190+
191+ // 5. Generate document description
146192 self . generate_description ( ctx) ;
147193
148194 let duration = start. elapsed ( ) . as_millis ( ) as u64 ;
@@ -158,7 +204,42 @@ impl IndexStage for EnrichStage {
158204 stage_result
159205 . metadata
160206 . insert ( "node_count" . to_string ( ) , serde_json:: json!( node_count) ) ;
207+ stage_result
208+ . metadata
209+ . insert ( "resolved_references" . to_string ( ) , serde_json:: json!( resolved_refs) ) ;
161210
162211 Ok ( stage_result)
163212 }
164213}
214+
215+ #[ cfg( test) ]
216+ mod tests {
217+ use super :: * ;
218+
219+ #[ test]
220+ fn test_resolve_references_section_ref ( ) {
221+ let mut tree = DocumentTree :: new ( "Root" , "root content" ) ;
222+ let s1 = tree. add_child ( tree. root ( ) , "Introduction" , "Introduction text." ) ;
223+ tree. set_structure ( s1, "1" ) ;
224+ let s2 = tree. add_child ( tree. root ( ) , "Details" , "For details, see Section 1 for more info" ) ;
225+ tree. set_structure ( s2, "2" ) ;
226+
227+ let resolved = EnrichStage :: resolve_references ( & mut tree) ;
228+ assert_eq ! ( resolved, 1 ) ;
229+
230+ // Verify the reference was stored on s2 and resolved to s1
231+ let refs = tree. get ( s2) . unwrap ( ) . references . clone ( ) ;
232+ assert_eq ! ( refs. len( ) , 1 ) ;
233+ assert_eq ! ( refs[ 0 ] . ref_type, RefType :: Section ) ;
234+ assert_eq ! ( refs[ 0 ] . target_node, Some ( s1) ) ;
235+ }
236+
237+ #[ test]
238+ fn test_resolve_references_no_refs ( ) {
239+ let mut tree = DocumentTree :: new ( "Root" , "root content" ) ;
240+ tree. add_child ( tree. root ( ) , "Section 1" , "No references here." ) ;
241+
242+ let resolved = EnrichStage :: resolve_references ( & mut tree) ;
243+ assert_eq ! ( resolved, 0 ) ;
244+ }
245+ }
0 commit comments