Skip to content

Commit c7b1569

Browse files
committed
fix: replace priority-based search ranking with boost-based interleaving
Old ranking used hard source priority (all memories before all facts before all messages), so message hits could never appear when 10+ memories existed. New ranking uses score * source_boost (memory=1.3, fact=1.15, message=1.0) so strong message matches can beat weak memory matches while still favoring higher-signal sources.
1 parent 0be7c14 commit c7b1569

File tree

5 files changed

+29
-20
lines changed

5 files changed

+29
-20
lines changed

scripts/context-dump/metrics.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,14 @@ export function buildDumpStats(originalMessages: DumpMessage[], transformedMessa
9898
.map((msg, index) => {
9999
const { id } = messageIdentity(msg, index)
100100
const tokens = msg.info.tokens as Record<string, unknown>
101+
const error = msg.info.error;
101102
const cache = isRecord(tokens.cache) ? tokens.cache : {}
102103
const total = typeof tokens.total === "number" ? tokens.total : 0
103104
const input = typeof tokens.input === "number" ? tokens.input : 0
104105
const output = typeof tokens.output === "number" ? tokens.output : 0
105106
const cacheRead = typeof cache.read === "number" ? cache.read : 0
106107
const cacheWrite = typeof cache.write === "number" ? cache.write : 0
107-
const cacheBust = prevCacheRead > 0 && cacheRead < prevCacheRead
108+
const cacheBust = prevCacheRead > 0 && cacheRead < prevCacheRead && (!error || error.name != "MessageAbortedError")
108109
prevCacheRead = cacheRead
109110

110111
const time = isRecord(msg.info.time) ? msg.info.time : {}

scripts/context-dump/run-context-dump.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,8 @@ export async function runContextDump(sessionId: string): Promise<ContextDumpResu
195195
historianWriteTimes,
196196
previousBustTime,
197197
)
198+
//console.dir(entry);
199+
198200
previousBustTime = entry.time_completed
199201
return [{ entry, probableCause }]
200202
})

scripts/context-dump/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@ export interface DumpMessageInfo {
22
id?: string
33
role?: string
44
sessionID?: string
5+
error?: MsgError
56
[key: string]: unknown
67
}
78

9+
export interface MsgError {
10+
name: string;
11+
data: unknown[];
12+
}
813
export interface DumpMessage {
914
info: DumpMessageInfo
1015
parts: unknown[]

src/features/magic-context/search.test.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,14 @@ describe("unifiedSearch", () => {
9191
});
9292

9393
expect(results).toHaveLength(4);
94-
expect(results[0]?.source).toBe("memory");
95-
expect(results[1]?.source).toBe("fact");
96-
expect(results[2]?.source).toBe("message");
97-
expect(results[3]?.source).toBe("message");
98-
expect(results[2]).toMatchObject({ messageOrdinal: 1 });
99-
expect(results[3]).toMatchObject({ messageOrdinal: 2 });
94+
const sources = results.map((r) => r.source);
95+
expect(sources).toContain("memory");
96+
expect(sources).toContain("fact");
97+
expect(sources).toContain("message");
98+
// With boost-based ranking, sources interleave by effective score (score * boost)
99+
// rather than strict priority ordering
100+
const messageResults = results.filter((r) => r.source === "message");
101+
expect(messageResults).toHaveLength(2);
100102
expect(embeddingQueries).toEqual(["ranked search"]);
101103
expect(getMemoryById(db, memory.id)?.retrievalCount).toBe(1);
102104
});

src/features/magic-context/search.ts

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ const SEMANTIC_WEIGHT = 0.7;
2222
const FTS_WEIGHT = 0.3;
2323
const SINGLE_SOURCE_PENALTY = 0.8;
2424
const RESULT_PREVIEW_LIMIT = 220;
25-
const MEMORY_SOURCE_PRIORITY = 0;
26-
const FACT_SOURCE_PRIORITY = 1;
27-
const MESSAGE_SOURCE_PRIORITY = 2;
25+
/** Source boost multipliers for unified ranking — higher-signal sources get a mild score boost. */
26+
const MEMORY_SOURCE_BOOST = 1.3;
27+
const FACT_SOURCE_BOOST = 1.15;
28+
const MESSAGE_SOURCE_BOOST = 1.0;
2829

2930
interface MessageSearchRow {
3031
messageOrdinal?: number | string;
@@ -378,25 +379,23 @@ function searchMessages(args: {
378379
.filter((result): result is MessageSearchResult => result !== null);
379380
}
380381

381-
function getSourcePriority(result: UnifiedSearchResult): number {
382+
function getSourceBoost(result: UnifiedSearchResult): number {
382383
switch (result.source) {
383384
case "memory":
384-
return MEMORY_SOURCE_PRIORITY;
385+
return MEMORY_SOURCE_BOOST;
385386
case "fact":
386-
return FACT_SOURCE_PRIORITY;
387+
return FACT_SOURCE_BOOST;
387388
case "message":
388-
return MESSAGE_SOURCE_PRIORITY;
389+
return MESSAGE_SOURCE_BOOST;
389390
}
390391
}
391392

392393
function compareUnifiedResults(left: UnifiedSearchResult, right: UnifiedSearchResult): number {
393-
const priorityDiff = getSourcePriority(left) - getSourcePriority(right);
394-
if (priorityDiff !== 0) {
395-
return priorityDiff;
396-
}
394+
const leftEffective = left.score * getSourceBoost(left);
395+
const rightEffective = right.score * getSourceBoost(right);
397396

398-
if (right.score !== left.score) {
399-
return right.score - left.score;
397+
if (rightEffective !== leftEffective) {
398+
return rightEffective - leftEffective;
400399
}
401400

402401
if (left.source === "memory" && right.source === "memory") {

0 commit comments

Comments
 (0)