{"version":3,"file":"retrieval.d.ts","sourceRoot":"","sources":["../../../src/core/evaluation/retrieval.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,uBAAuB,EAAE,MAAM,CAAC;CAChC;AAMD,wBAAgB,yBAAyB,CAAC,KAAK,EAAE;IAChD,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,CAAC,EAAE,MAAM,CAAC;CACV,GAAG,gBAAgB,CAyCnB","sourcesContent":["export interface RetrievalLabel {\n\tresultId: string;\n\trelevance: number;\n\tcurrent: boolean;\n}\n\nexport interface RetrievalMetrics {\n\trecallAtK: number;\n\tprecisionAtK: number;\n\tmrr: number;\n\tndcg: number;\n\texactSymbolHitRate: number;\n\trelevantFileHitRate: number;\n\tstaleResultRate: number;\n\tduplicateResultRate: number;\n\tcontextTokenEfficiency: number;\n\trevalidationSuccessRate: number;\n}\n\nfunction unique<T>(values: T[]): T[] {\n\treturn [...new Set(values)];\n}\n\nexport function calculateRetrievalMetrics(input: {\n\tresults: RetrievalLabel[];\n\trelevantResultIds: string[];\n\trelevantSymbolIds?: string[];\n\treturnedSymbolIds?: string[];\n\trelevantFileIds?: string[];\n\treturnedFileIds?: string[];\n\tcontextTokens?: number;\n\trevalidatedResultIds?: string[];\n\tk: number;\n}): RetrievalMetrics {\n\tif (!Number.isInteger(input.k) || input.k <= 0) throw new Error(\"retrieval k must be a positive integer\");\n\tconst top = input.results.slice(0, input.k);\n\tconst relevant = new Set(input.relevantResultIds);\n\tconst topIds = top.map((result) => result.resultId);\n\tconst uniqueTopIds = unique(topIds);\n\tconst hits = uniqueTopIds.filter((id) => relevant.has(id)).length;\n\tconst rankedHit = top.findIndex((result) => relevant.has(result.resultId) && result.current);\n\tconst ideal = [...input.results]\n\t\t.filter((result) => result.current)\n\t\t.sort((left, right) => right.relevance - left.relevance)\n\t\t.slice(0, input.k)\n\t\t.map((result) => result.relevance);\n\tconst dcg = top.reduce((sum, result, index) => sum + (2 ** result.relevance - 1) / Math.log2(index + 2), 0);\n\tconst idcg = ideal.reduce((sum, relevance, index) => sum + (2 ** relevance - 1) / Math.log2(index + 2), 0);\n\tconst relevantSymbols = unique(input.relevantSymbolIds ?? []);\n\tconst returnedSymbols = unique(input.returnedSymbolIds ?? []);\n\tconst relevantFiles = unique(input.relevantFileIds ?? []);\n\tconst returnedFiles = unique(input.returnedFileIds ?? []);\n\tconst revalidated = new Set(input.revalidatedResultIds ?? []);\n\tconst currentRelevant = input.results.filter((result) => relevant.has(result.resultId) && result.current).length;\n\treturn {\n\t\trecallAtK: relevant.size === 0 ? 0 : hits / relevant.size,\n\t\tprecisionAtK: input.k === 0 ? 0 : hits / input.k,\n\t\tmrr: rankedHit === -1 ? 0 : 1 / (rankedHit + 1),\n\t\tndcg: idcg === 0 ? 0 : dcg / idcg,\n\t\texactSymbolHitRate:\n\t\t\trelevantSymbols.length === 0\n\t\t\t\t? 0\n\t\t\t\t: returnedSymbols.filter((id) => relevantSymbols.includes(id)).length / relevantSymbols.length,\n\t\trelevantFileHitRate:\n\t\t\trelevantFiles.length === 0\n\t\t\t\t? 0\n\t\t\t\t: returnedFiles.filter((id) => relevantFiles.includes(id)).length / relevantFiles.length,\n\t\tstaleResultRate: top.length === 0 ? 0 : top.filter((result) => !result.current).length / top.length,\n\t\tduplicateResultRate: topIds.length === 0 ? 0 : (topIds.length - uniqueTopIds.length) / topIds.length,\n\t\tcontextTokenEfficiency:\n\t\t\tinput.contextTokens && input.contextTokens > 0 ? currentRelevant / input.contextTokens : 0,\n\t\trevalidationSuccessRate:\n\t\t\ttop.length === 0 ? 0 : top.filter((result) => revalidated.has(result.resultId)).length / top.length,\n\t};\n}\n"]}