( graph: GraphStore, nodeId: string, queryVec: number[], depth: number, maxDepth: number, pathLabels: string[], visited: Set<string>, results: TraversalResult[], edgeFilter?: RelationType[], )
| 218 | } |
| 219 | |
| 220 | function traverseNeighbors( |
| 221 | graph: GraphStore, nodeId: string, queryVec: number[], depth: number, maxDepth: number, |
| 222 | pathLabels: string[], visited: Set<string>, results: TraversalResult[], edgeFilter?: RelationType[], |
| 223 | ): void { |
| 224 | if (depth > maxDepth) return; |
| 225 | |
| 226 | for (const edge of getEdgesForNode(graph, nodeId)) { |
| 227 | if (edgeFilter && !edgeFilter.includes(edge.relation)) continue; |
| 228 | const neighborId = getNeighborId(edge, nodeId); |
| 229 | if (visited.has(neighborId)) continue; |
| 230 | |
| 231 | const neighbor = graph.nodes[neighborId]; |
| 232 | if (!neighbor) continue; |
| 233 | |
| 234 | visited.add(neighborId); |
| 235 | const similarity = cosine(queryVec, neighbor.embedding); |
| 236 | const edgeDecay = decayWeight(edge); |
| 237 | const relevance = similarity * 0.6 + (edgeDecay / Math.max(edge.weight, 0.01)) * 0.4; |
| 238 | |
| 239 | results.push({ |
| 240 | node: neighbor, |
| 241 | depth, |
| 242 | pathRelations: [...pathLabels, `--[${edge.relation}]-->`, neighbor.label], |
| 243 | relevanceScore: Math.round(relevance * 1000) / 10, |
| 244 | }); |
| 245 | |
| 246 | neighbor.lastAccessed = Date.now(); |
| 247 | traverseNeighbors(graph, neighborId, queryVec, depth + 1, maxDepth, [...pathLabels, `--[${edge.relation}]-->`, neighbor.label], visited, results, edgeFilter); |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | export async function pruneStaleLinks(rootDir: string, threshold?: number): Promise<{ removed: number; remaining: number }> { |
| 252 | const graph = await loadGraph(rootDir); |
no test coverage detected