rag_plusplus_core/trajectory/chain/
links.rs1use crate::trajectory::graph::NodeId;
7use super::manager::ChainId;
8
9#[derive(Debug, Clone, Copy, PartialEq)]
11pub struct LinkStrength {
12 pub semantic: f32,
14 pub temporal: f32,
16 pub thematic: f32,
18 pub combined: f32,
20}
21
22impl LinkStrength {
23 pub fn new(semantic: f32, temporal: f32, thematic: f32) -> Self {
25 let combined = 0.5 * semantic + 0.2 * temporal + 0.3 * thematic;
27 Self {
28 semantic,
29 temporal,
30 thematic,
31 combined,
32 }
33 }
34
35 pub fn from_semantic(similarity: f32) -> Self {
37 Self::new(similarity, 0.5, 0.5)
38 }
39
40 pub fn is_strong(&self, threshold: f32) -> bool {
42 self.combined >= threshold
43 }
44}
45
46impl Default for LinkStrength {
47 fn default() -> Self {
48 Self {
49 semantic: 0.0,
50 temporal: 0.0,
51 thematic: 0.0,
52 combined: 0.0,
53 }
54 }
55}
56
57#[derive(Debug, Clone)]
62pub struct CrossChainLink {
63 pub source_chain: ChainId,
65 pub source_node: NodeId,
67 pub target_chain: ChainId,
69 pub target_node: NodeId,
71 pub strength: LinkStrength,
73 pub link_type: CrossChainLinkType,
75 pub description: Option<String>,
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
81pub enum CrossChainLinkType {
82 Semantic,
84 Reference,
86 Continuation,
88 Related,
90 Alternative,
92 KnowledgeTransfer,
94}
95
96impl CrossChainLink {
97 pub fn new(
99 source_chain: ChainId,
100 source_node: NodeId,
101 target_chain: ChainId,
102 target_node: NodeId,
103 strength: LinkStrength,
104 link_type: CrossChainLinkType,
105 ) -> Self {
106 Self {
107 source_chain,
108 source_node,
109 target_chain,
110 target_node,
111 strength,
112 link_type,
113 description: None,
114 }
115 }
116
117 pub fn semantic(
119 source_chain: ChainId,
120 source_node: NodeId,
121 target_chain: ChainId,
122 target_node: NodeId,
123 similarity: f32,
124 ) -> Self {
125 Self::new(
126 source_chain,
127 source_node,
128 target_chain,
129 target_node,
130 LinkStrength::from_semantic(similarity),
131 CrossChainLinkType::Semantic,
132 )
133 }
134
135 pub fn with_description(mut self, description: impl Into<String>) -> Self {
137 self.description = Some(description.into());
138 self
139 }
140
141 pub fn is_bidirectional(&self) -> bool {
143 matches!(
145 self.link_type,
146 CrossChainLinkType::Semantic | CrossChainLinkType::Related
147 )
148 }
149
150 pub fn reverse(&self) -> Self {
152 Self {
153 source_chain: self.target_chain.clone(),
154 source_node: self.target_node,
155 target_chain: self.source_chain.clone(),
156 target_node: self.source_node,
157 strength: self.strength,
158 link_type: self.link_type,
159 description: self.description.clone(),
160 }
161 }
162}
163
164pub fn find_cross_chain_links(
180 _chain_embeddings: &[(ChainId, Vec<(NodeId, Vec<f32>)>)],
181 _threshold: f32,
182) -> Vec<CrossChainLink> {
183 Vec::new()
189}
190
191pub const KNOWLEDGE_TRANSFER_PATTERNS: &[&str] = &[
196 "as we discussed",
197 "like we did before",
198 "similar to what we",
199 "remember when we",
200 "building on the",
201 "like in the",
202 "same as before",
203 "just like last time",
204 "from our previous",
205 "we already covered",
206];
207
208pub fn detect_knowledge_transfer(text: &str) -> Option<&'static str> {
210 let lower = text.to_lowercase();
211 for pattern in KNOWLEDGE_TRANSFER_PATTERNS {
212 if lower.contains(pattern) {
213 return Some(pattern);
214 }
215 }
216 None
217}
218
219#[cfg(test)]
220mod tests {
221 use super::*;
222
223 #[test]
224 fn test_link_strength() {
225 let strength = LinkStrength::new(0.8, 0.5, 0.6);
226 assert!(strength.combined > 0.0);
227 assert!(strength.is_strong(0.5));
228 assert!(!strength.is_strong(0.9));
229 }
230
231 #[test]
232 fn test_cross_chain_link() {
233 let link = CrossChainLink::semantic(
234 "chain1".to_string(),
235 1,
236 "chain2".to_string(),
237 2,
238 0.85,
239 );
240
241 assert_eq!(link.source_chain, "chain1");
242 assert_eq!(link.target_chain, "chain2");
243 assert!(link.is_bidirectional());
244
245 let reverse = link.reverse();
246 assert_eq!(reverse.source_chain, "chain2");
247 assert_eq!(reverse.target_chain, "chain1");
248 }
249
250 #[test]
251 fn test_knowledge_transfer_detection() {
252 assert!(detect_knowledge_transfer("As we discussed before, this should work").is_some());
253 assert!(detect_knowledge_transfer("Similar to what we did in the auth project").is_some());
254 assert!(detect_knowledge_transfer("This is completely new").is_none());
255 }
256
257 #[test]
258 fn test_link_with_description() {
259 let link = CrossChainLink::semantic(
260 "chain1".to_string(),
261 1,
262 "chain2".to_string(),
263 2,
264 0.85,
265 ).with_description("Both discuss authentication");
266
267 assert_eq!(link.description, Some("Both discuss authentication".to_string()));
268 }
269}