From 66e66f19c6720e9302397aa65f30d4f1f99e993e Mon Sep 17 00:00:00 2001 From: Alex Fries Date: Sat, 25 Apr 2026 02:46:03 -0400 Subject: [PATCH] feat(memory-core): expose hybrid search component scores Expose raw `vectorScore` and `textScore` alongside the combined hybrid memory search `score`. - Preserve vector/text component scores from `mergeHybridResults` output. - Add optional component-score fields to both memory host SDK type surfaces. - Extend hybrid merge tests for vector-only, text-only, and overlapping result cases. - Document that component scores remain raw retrieval diagnostics while temporal decay/MMR only adjust or reorder the combined ranking `score`. Closes #68166. Maintainer verification: - `pnpm test extensions/memory-core/src/memory/hybrid.test.ts` - `pnpm check:changed` - Fresh GitHub checks passed. Co-authored-by: Alex Fries --- extensions/memory-core/src/memory/hybrid.test.ts | 6 ++++++ extensions/memory-core/src/memory/hybrid.ts | 6 ++++++ packages/memory-host-sdk/src/host/types.ts | 2 ++ src/memory-host-sdk/host/types.ts | 2 ++ 4 files changed, 16 insertions(+) diff --git a/extensions/memory-core/src/memory/hybrid.test.ts b/extensions/memory-core/src/memory/hybrid.test.ts index 134e7bfe7eb..deb9f947710 100644 --- a/extensions/memory-core/src/memory/hybrid.test.ts +++ b/extensions/memory-core/src/memory/hybrid.test.ts @@ -60,7 +60,11 @@ describe("memory hybrid helpers", () => { const a = merged.find((r) => r.path === "memory/a.md"); const b = merged.find((r) => r.path === "memory/b.md"); expect(a?.score).toBeCloseTo(0.7 * 0.9); + expect(a?.vectorScore).toBeCloseTo(0.9); + expect(a?.textScore).toBe(0); expect(b?.score).toBeCloseTo(0.3 * 1.0); + expect(b?.vectorScore).toBe(0); + expect(b?.textScore).toBeCloseTo(1.0); }); it("mergeHybridResults prefers keyword snippet when ids overlap", async () => { @@ -94,5 +98,7 @@ describe("memory hybrid helpers", () => { expect(merged).toHaveLength(1); expect(merged[0]?.snippet).toBe("kw-a"); expect(merged[0]?.score).toBeCloseTo(0.5 * 0.2 + 0.5 * 1.0); + expect(merged[0]?.vectorScore).toBeCloseTo(0.2); + expect(merged[0]?.textScore).toBeCloseTo(1.0); }); }); diff --git a/extensions/memory-core/src/memory/hybrid.ts b/extensions/memory-core/src/memory/hybrid.ts index 209a6bc3f31..5d84ca42101 100644 --- a/extensions/memory-core/src/memory/hybrid.ts +++ b/extensions/memory-core/src/memory/hybrid.ts @@ -72,6 +72,8 @@ export async function mergeHybridResults(params: { startLine: number; endLine: number; score: number; + vectorScore: number; + textScore: number; snippet: string; source: HybridSource; }> @@ -131,11 +133,15 @@ export async function mergeHybridResults(params: { startLine: entry.startLine, endLine: entry.endLine, score, + vectorScore: entry.vectorScore, + textScore: entry.textScore, snippet: entry.snippet, source: entry.source, }; }); + // Keep component scores as raw retrieval diagnostics; temporal decay and MMR + // only adjust or reorder the combined ranking score. const temporalDecayConfig = { ...DEFAULT_TEMPORAL_DECAY_CONFIG, ...params.temporalDecay }; const decayed = await applyTemporalDecayToHybridResults({ results: merged, diff --git a/packages/memory-host-sdk/src/host/types.ts b/packages/memory-host-sdk/src/host/types.ts index 534a914e20a..602a5a1b1d0 100644 --- a/packages/memory-host-sdk/src/host/types.ts +++ b/packages/memory-host-sdk/src/host/types.ts @@ -5,6 +5,8 @@ export type MemorySearchResult = { startLine: number; endLine: number; score: number; + vectorScore?: number; + textScore?: number; snippet: string; source: MemorySource; citation?: string; diff --git a/src/memory-host-sdk/host/types.ts b/src/memory-host-sdk/host/types.ts index b7a068fc3d8..0f70b9f2c4d 100644 --- a/src/memory-host-sdk/host/types.ts +++ b/src/memory-host-sdk/host/types.ts @@ -5,6 +5,8 @@ export type MemorySearchResult = { startLine: number; endLine: number; score: number; + vectorScore?: number; + textScore?: number; snippet: string; source: MemorySource; citation?: string;