| 40 | } |
| 41 | |
| 42 | export function scoreFindRelevantContext( |
| 43 | caseId: string, |
| 44 | expectedSymbols: string[], |
| 45 | subgraph: { nodes: Map<string, { name: string }>; edges: unknown[]; roots: string[] }, |
| 46 | latencyMs: number |
| 47 | ): EvalResult { |
| 48 | const expectedLower = new Set(expectedSymbols.map((s) => s.toLowerCase())); |
| 49 | const nodeNames = new Set<string>(); |
| 50 | for (const node of subgraph.nodes.values()) { |
| 51 | nodeNames.add(node.name.toLowerCase()); |
| 52 | } |
| 53 | |
| 54 | const found: string[] = []; |
| 55 | const missed: string[] = []; |
| 56 | |
| 57 | for (const sym of expectedSymbols) { |
| 58 | if (nodeNames.has(sym.toLowerCase())) { |
| 59 | found.push(sym); |
| 60 | } else { |
| 61 | missed.push(sym); |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | const recall = expectedSymbols.length > 0 ? found.length / expectedSymbols.length : 0; |
| 66 | const nodeCount = subgraph.nodes.size; |
| 67 | const edgeCount = subgraph.edges.length; |
| 68 | const edgeDensity = nodeCount > 0 ? edgeCount / nodeCount : 0; |
| 69 | |
| 70 | return { |
| 71 | caseId, |
| 72 | pass: recall >= PASS_THRESHOLD, |
| 73 | recall, |
| 74 | mrr: 0, |
| 75 | foundSymbols: found, |
| 76 | missedSymbols: missed, |
| 77 | nodeCount, |
| 78 | edgeCount, |
| 79 | edgeDensity, |
| 80 | latencyMs, |
| 81 | }; |
| 82 | } |