@mneme-ai/core 0.8.3 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/entities/go-parser.d.ts +47 -0
- package/dist/entities/go-parser.d.ts.map +1 -0
- package/dist/entities/go-parser.js +315 -0
- package/dist/entities/go-parser.js.map +1 -0
- package/dist/entities/go-parser.test.d.ts +2 -0
- package/dist/entities/go-parser.test.d.ts.map +1 -0
- package/dist/entities/go-parser.test.js +147 -0
- package/dist/entities/go-parser.test.js.map +1 -0
- package/dist/entities/index.d.ts +1 -0
- package/dist/entities/index.d.ts.map +1 -1
- package/dist/entities/index.js +1 -0
- package/dist/entities/index.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/indexer/indexer.d.ts +12 -0
- package/dist/indexer/indexer.d.ts.map +1 -1
- package/dist/indexer/indexer.js +28 -1
- package/dist/indexer/indexer.js.map +1 -1
- package/dist/insights/decisions.d.ts +38 -0
- package/dist/insights/decisions.d.ts.map +1 -0
- package/dist/insights/decisions.js +125 -0
- package/dist/insights/decisions.js.map +1 -0
- package/dist/insights/decisions.test.d.ts +2 -0
- package/dist/insights/decisions.test.d.ts.map +1 -0
- package/dist/insights/decisions.test.js +141 -0
- package/dist/insights/decisions.test.js.map +1 -0
- package/dist/insights/dream.d.ts +71 -0
- package/dist/insights/dream.d.ts.map +1 -0
- package/dist/insights/dream.js +235 -0
- package/dist/insights/dream.js.map +1 -0
- package/dist/insights/dream.test.d.ts +2 -0
- package/dist/insights/dream.test.d.ts.map +1 -0
- package/dist/insights/dream.test.js +127 -0
- package/dist/insights/dream.test.js.map +1 -0
- package/dist/insights/index.d.ts +16 -0
- package/dist/insights/index.d.ts.map +1 -0
- package/dist/insights/index.js +16 -0
- package/dist/insights/index.js.map +1 -0
- package/dist/insights/obsidian.d.ts +42 -0
- package/dist/insights/obsidian.d.ts.map +1 -0
- package/dist/insights/obsidian.js +263 -0
- package/dist/insights/obsidian.js.map +1 -0
- package/dist/insights/obsidian.test.d.ts +2 -0
- package/dist/insights/obsidian.test.d.ts.map +1 -0
- package/dist/insights/obsidian.test.js +241 -0
- package/dist/insights/obsidian.test.js.map +1 -0
- package/dist/insights/stack-trace.d.ts +40 -0
- package/dist/insights/stack-trace.d.ts.map +1 -0
- package/dist/insights/stack-trace.js +127 -0
- package/dist/insights/stack-trace.js.map +1 -0
- package/dist/insights/stack-trace.test.d.ts +2 -0
- package/dist/insights/stack-trace.test.d.ts.map +1 -0
- package/dist/insights/stack-trace.test.js +103 -0
- package/dist/insights/stack-trace.test.js.map +1 -0
- package/dist/insights/story.d.ts +34 -0
- package/dist/insights/story.d.ts.map +1 -0
- package/dist/insights/story.js +100 -0
- package/dist/insights/story.js.map +1 -0
- package/dist/insights/story.test.d.ts +2 -0
- package/dist/insights/story.test.d.ts.map +1 -0
- package/dist/insights/story.test.js +99 -0
- package/dist/insights/story.test.js.map +1 -0
- package/dist/insights/suggest.d.ts +29 -0
- package/dist/insights/suggest.d.ts.map +1 -0
- package/dist/insights/suggest.js +93 -0
- package/dist/insights/suggest.js.map +1 -0
- package/dist/insights/suggest.test.d.ts +2 -0
- package/dist/insights/suggest.test.d.ts.map +1 -0
- package/dist/insights/suggest.test.js +71 -0
- package/dist/insights/suggest.test.js.map +1 -0
- package/dist/insights/who-knows.d.ts +48 -0
- package/dist/insights/who-knows.d.ts.map +1 -0
- package/dist/insights/who-knows.js +96 -0
- package/dist/insights/who-knows.js.map +1 -0
- package/dist/insights/who-knows.test.d.ts +2 -0
- package/dist/insights/who-knows.test.d.ts.map +1 -0
- package/dist/insights/who-knows.test.js +47 -0
- package/dist/insights/who-knows.test.js.map +1 -0
- package/dist/retrieve/index.d.ts +2 -0
- package/dist/retrieve/index.d.ts.map +1 -1
- package/dist/retrieve/index.js +2 -0
- package/dist/retrieve/index.js.map +1 -1
- package/dist/retrieve/intent.d.ts +32 -0
- package/dist/retrieve/intent.d.ts.map +1 -0
- package/dist/retrieve/intent.js +104 -0
- package/dist/retrieve/intent.js.map +1 -0
- package/dist/retrieve/intent.test.d.ts +2 -0
- package/dist/retrieve/intent.test.d.ts.map +1 -0
- package/dist/retrieve/intent.test.js +106 -0
- package/dist/retrieve/intent.test.js.map +1 -0
- package/dist/retrieve/search.d.ts +30 -0
- package/dist/retrieve/search.d.ts.map +1 -1
- package/dist/retrieve/search.js +48 -0
- package/dist/retrieve/search.js.map +1 -1
- package/dist/retrieve/search.test.js +84 -1
- package/dist/retrieve/search.test.js.map +1 -1
- package/dist/retrieve/synthesize.d.ts +57 -0
- package/dist/retrieve/synthesize.d.ts.map +1 -0
- package/dist/retrieve/synthesize.js +160 -0
- package/dist/retrieve/synthesize.js.map +1 -0
- package/dist/retrieve/synthesize.test.d.ts +2 -0
- package/dist/retrieve/synthesize.test.d.ts.map +1 -0
- package/dist/retrieve/synthesize.test.js +116 -0
- package/dist/retrieve/synthesize.test.js.map +1 -0
- package/dist/store/schema.d.ts +2 -2
- package/dist/store/schema.d.ts.map +1 -1
- package/dist/store/schema.js +55 -1
- package/dist/store/schema.js.map +1 -1
- package/dist/store/sqlite.test.js +1 -1
- package/dist/util/index.d.ts +2 -0
- package/dist/util/index.d.ts.map +1 -1
- package/dist/util/index.js +2 -1
- package/dist/util/index.js.map +1 -1
- package/dist/util/redact.d.ts +58 -0
- package/dist/util/redact.d.ts.map +1 -0
- package/dist/util/redact.js +129 -0
- package/dist/util/redact.js.map +1 -0
- package/dist/util/redact.test.d.ts +2 -0
- package/dist/util/redact.test.d.ts.map +1 -0
- package/dist/util/redact.test.js +148 -0
- package/dist/util/redact.test.js.map +1 -0
- package/dist/wisdom/calibrator.d.ts +43 -0
- package/dist/wisdom/calibrator.d.ts.map +1 -0
- package/dist/wisdom/calibrator.js +120 -0
- package/dist/wisdom/calibrator.js.map +1 -0
- package/dist/wisdom/feedback.d.ts +45 -0
- package/dist/wisdom/feedback.d.ts.map +1 -0
- package/dist/wisdom/feedback.js +116 -0
- package/dist/wisdom/feedback.js.map +1 -0
- package/dist/wisdom/index.d.ts +15 -0
- package/dist/wisdom/index.d.ts.map +1 -0
- package/dist/wisdom/index.js +15 -0
- package/dist/wisdom/index.js.map +1 -0
- package/dist/wisdom/types.d.ts +67 -0
- package/dist/wisdom/types.d.ts.map +1 -0
- package/dist/wisdom/types.js +20 -0
- package/dist/wisdom/types.js.map +1 -0
- package/dist/wisdom/wisdom.test.d.ts +2 -0
- package/dist/wisdom/wisdom.test.d.ts.map +1 -0
- package/dist/wisdom/wisdom.test.js +144 -0
- package/dist/wisdom/wisdom.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -7,7 +7,37 @@ export interface SearchOptions {
|
|
|
7
7
|
topK?: number;
|
|
8
8
|
/** Weight for semantic vs lexical (0 = pure lexical, 1 = pure semantic). */
|
|
9
9
|
semanticWeight?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Confidence floor — return [] instead of low-confidence guesses when the
|
|
12
|
+
* query has no real signal in the corpus. Critical for honest "no context
|
|
13
|
+
* found" answers on gibberish/out-of-distribution queries.
|
|
14
|
+
*
|
|
15
|
+
* "auto" (default) — empty if (no FTS hits) AND (top semantic cosine < 0.4)
|
|
16
|
+
* "off" — never filter; return whatever fusion produced
|
|
17
|
+
* { ... } — explicit thresholds
|
|
18
|
+
*/
|
|
19
|
+
confidenceFloor?: "auto" | "off" | {
|
|
20
|
+
minFtsHits: number;
|
|
21
|
+
minSemCosine: number;
|
|
22
|
+
};
|
|
10
23
|
}
|
|
24
|
+
/** Thresholds used when confidenceFloor === "auto". Calibrated from the eval set. */
|
|
25
|
+
export declare const DEFAULT_CONFIDENCE: {
|
|
26
|
+
minFtsHits: number;
|
|
27
|
+
minSemCosine: number;
|
|
28
|
+
};
|
|
29
|
+
/** Confidence label assigned to a result set based on score distribution. */
|
|
30
|
+
export type ConfidenceLabel = "high" | "medium" | "low" | "none";
|
|
31
|
+
/**
|
|
32
|
+
* Adaptive confidence — looks at the score gap between top-1 and top-3 in
|
|
33
|
+
* addition to the absolute top score. Hard thresholds alone are misleading:
|
|
34
|
+
* - "stripe bigint" returns one strong match (top=0.025, second=0.005) → high
|
|
35
|
+
* - "how to improve code" returns 8 ties (all ≈ 0.016) → low
|
|
36
|
+
*
|
|
37
|
+
* Calibrated empirically on the 50-question eval set + the production query
|
|
38
|
+
* "how to improve my code" that exposed the original regression.
|
|
39
|
+
*/
|
|
40
|
+
export declare function classifyConfidence(results: SearchResult[]): ConfidenceLabel;
|
|
11
41
|
/**
|
|
12
42
|
* Hybrid retrieval: combine FTS (BM25) with vector cosine, fuse with weighted RRF.
|
|
13
43
|
* Falls back to FTS-only if no embedder is configured or no embeddings exist.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/retrieve/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EAGT,WAAW,EACX,iBAAiB,EACjB,QAAQ,EACR,YAAY,EACb,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/retrieve/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EAGT,WAAW,EACX,iBAAiB,EACjB,QAAQ,EACR,YAAY,EACb,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;CACjF;AAED,qFAAqF;AACrF,eAAO,MAAM,kBAAkB,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAG1E,CAAC;AAEF,6EAA6E;AAC7E,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;AAEjE;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,eAAe,CAmB3E;AAED;;;GAGG;AACH,wBAAsB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CA6ExF;AAED,wBAAsB,GAAG,CACvB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,SAAS,CAAC,CAQpB;AAiDD,wBAAgB,MAAM,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,GAAG,MAAM,CAc/D;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,WAAW,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,QAAQ,EAAE,EACf,GAAG,EAAE,QAAQ,EAAE,EACf,GAAG,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAChE,KAAK,CAAC;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAW9C"}
|
package/dist/retrieve/search.js
CHANGED
|
@@ -1,3 +1,39 @@
|
|
|
1
|
+
/** Thresholds used when confidenceFloor === "auto". Calibrated from the eval set. */
|
|
2
|
+
export const DEFAULT_CONFIDENCE = {
|
|
3
|
+
minFtsHits: 1,
|
|
4
|
+
minSemCosine: 0.4,
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Adaptive confidence — looks at the score gap between top-1 and top-3 in
|
|
8
|
+
* addition to the absolute top score. Hard thresholds alone are misleading:
|
|
9
|
+
* - "stripe bigint" returns one strong match (top=0.025, second=0.005) → high
|
|
10
|
+
* - "how to improve code" returns 8 ties (all ≈ 0.016) → low
|
|
11
|
+
*
|
|
12
|
+
* Calibrated empirically on the 50-question eval set + the production query
|
|
13
|
+
* "how to improve my code" that exposed the original regression.
|
|
14
|
+
*/
|
|
15
|
+
export function classifyConfidence(results) {
|
|
16
|
+
if (results.length === 0)
|
|
17
|
+
return "none";
|
|
18
|
+
const top = results[0].score;
|
|
19
|
+
// Absolute floor — anything below this is noise regardless of gap.
|
|
20
|
+
if (top < 0.005)
|
|
21
|
+
return "none";
|
|
22
|
+
// Gap-based: if top is much higher than the average of the next 2-4
|
|
23
|
+
// results, the system has a clear winner. If results are tied (small gap),
|
|
24
|
+
// confidence drops even when top is decent.
|
|
25
|
+
const tail = results.slice(1, 5);
|
|
26
|
+
const tailMean = tail.length > 0 ? tail.reduce((s, r) => s + r.score, 0) / tail.length : 0;
|
|
27
|
+
const gap = top - tailMean;
|
|
28
|
+
const ratio = tailMean === 0 ? Infinity : top / tailMean;
|
|
29
|
+
if (top >= 0.025 && (ratio >= 1.4 || gap >= 0.005))
|
|
30
|
+
return "high";
|
|
31
|
+
if (top >= 0.012 && (ratio >= 1.2 || gap >= 0.002))
|
|
32
|
+
return "medium";
|
|
33
|
+
if (top >= 0.005)
|
|
34
|
+
return "low";
|
|
35
|
+
return "none";
|
|
36
|
+
}
|
|
1
37
|
/**
|
|
2
38
|
* Hybrid retrieval: combine FTS (BM25) with vector cosine, fuse with weighted RRF.
|
|
3
39
|
* Falls back to FTS-only if no embedder is configured or no embeddings exist.
|
|
@@ -12,6 +48,7 @@ export async function search(query, opts) {
|
|
|
12
48
|
raw: h.bm25,
|
|
13
49
|
}));
|
|
14
50
|
let semanticScored = [];
|
|
51
|
+
let topSemCosine = 0;
|
|
15
52
|
if (opts.embedder && opts.store.countChunksWithEmbedding() > 0) {
|
|
16
53
|
try {
|
|
17
54
|
const [qvec] = await opts.embedder.embed([query]);
|
|
@@ -31,6 +68,7 @@ export async function search(query, opts) {
|
|
|
31
68
|
});
|
|
32
69
|
}
|
|
33
70
|
sims.sort((a, b) => b.sim - a.sim);
|
|
71
|
+
topSemCosine = sims[0]?.sim ?? 0;
|
|
34
72
|
semanticScored = sims.slice(0, topK * 4).map((s, i) => ({ chunk: s.chunk, rank: i + 1, raw: s.sim }));
|
|
35
73
|
}
|
|
36
74
|
}
|
|
@@ -40,6 +78,16 @@ export async function search(query, opts) {
|
|
|
40
78
|
semanticScored = [];
|
|
41
79
|
}
|
|
42
80
|
}
|
|
81
|
+
// Honest "no context found" — block low-confidence guesses on gibberish queries.
|
|
82
|
+
// This is the difference between an AI tool and a slot machine.
|
|
83
|
+
const floor = opts.confidenceFloor ?? "auto";
|
|
84
|
+
if (floor !== "off") {
|
|
85
|
+
const cfg = floor === "auto" ? DEFAULT_CONFIDENCE : floor;
|
|
86
|
+
const noLex = ftsScored.length < cfg.minFtsHits;
|
|
87
|
+
const noSem = topSemCosine < cfg.minSemCosine;
|
|
88
|
+
if (noLex && noSem)
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
43
91
|
const fused = reciprocalRankFusion(ftsScored, semanticScored, {
|
|
44
92
|
lexicalWeight: 1 - semanticWeight,
|
|
45
93
|
semanticWeight,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/retrieve/search.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/retrieve/search.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { cosine, reciprocalRankFusion } from "./search.js";
|
|
2
|
+
import { cosine, reciprocalRankFusion, search, DEFAULT_CONFIDENCE } from "./search.js";
|
|
3
3
|
const chunk = (id, hash = "h") => ({
|
|
4
4
|
id,
|
|
5
5
|
commitHash: hash,
|
|
@@ -102,4 +102,87 @@ describe("reciprocalRankFusion", () => {
|
|
|
102
102
|
expect(out[0].score).toBeCloseTo(1 / 61, 6);
|
|
103
103
|
});
|
|
104
104
|
});
|
|
105
|
+
function makeStore(opts) {
|
|
106
|
+
const ftsHits = opts.ftsHits ?? [];
|
|
107
|
+
const chunks = opts.embeddedChunks ?? [];
|
|
108
|
+
return {
|
|
109
|
+
ftsSearch: () => ftsHits,
|
|
110
|
+
countChunksWithEmbedding: () => chunks.length,
|
|
111
|
+
iterEmbeddedChunks: function* () { for (const c of chunks)
|
|
112
|
+
yield c; },
|
|
113
|
+
getCommit: (hash) => ({
|
|
114
|
+
hash,
|
|
115
|
+
shortHash: hash.slice(0, 7),
|
|
116
|
+
subject: `commit ${hash}`,
|
|
117
|
+
authorName: "test",
|
|
118
|
+
authorDate: "2024-01-01",
|
|
119
|
+
body: "",
|
|
120
|
+
}),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
const fakeEmbedder = (vec) => ({
|
|
124
|
+
name: "fake",
|
|
125
|
+
dimensions: vec.length,
|
|
126
|
+
embed: async (texts) => texts.map(() => vec),
|
|
127
|
+
});
|
|
128
|
+
describe("search — confidence floor", () => {
|
|
129
|
+
it("returns [] for gibberish (no FTS hits + low semantic similarity)", async () => {
|
|
130
|
+
const queryVec = Float32Array.from([1, 0, 0, 0]);
|
|
131
|
+
const orthogonalVec = Float32Array.from([0, 1, 0, 0]); // cosine = 0, well below 0.4
|
|
132
|
+
const store = makeStore({
|
|
133
|
+
ftsHits: [],
|
|
134
|
+
embeddedChunks: [{ id: 1, commitHash: "abc", kind: "subject", text: "x", vec: orthogonalVec }],
|
|
135
|
+
});
|
|
136
|
+
const results = await search("xyzzyplugh meaningless gibberish", {
|
|
137
|
+
store,
|
|
138
|
+
embedder: fakeEmbedder(queryVec),
|
|
139
|
+
});
|
|
140
|
+
expect(results).toEqual([]);
|
|
141
|
+
});
|
|
142
|
+
it("returns results when FTS has hits even if semantic is weak", async () => {
|
|
143
|
+
const queryVec = Float32Array.from([1, 0, 0, 0]);
|
|
144
|
+
const weakSemVec = Float32Array.from([0.2, 0, 0, 0]); // cosine = 1.0 normalized — actually high
|
|
145
|
+
const store = makeStore({
|
|
146
|
+
ftsHits: [{ id: 1, commitHash: "abc", kind: "subject", text: "real match", bm25: 5.0 }],
|
|
147
|
+
embeddedChunks: [{ id: 1, commitHash: "abc", kind: "subject", text: "real match", vec: weakSemVec }],
|
|
148
|
+
});
|
|
149
|
+
const results = await search("real query with token matches", {
|
|
150
|
+
store,
|
|
151
|
+
embedder: fakeEmbedder(queryVec),
|
|
152
|
+
});
|
|
153
|
+
expect(results.length).toBeGreaterThan(0);
|
|
154
|
+
});
|
|
155
|
+
it("returns results when semantic is strong even if FTS is empty", async () => {
|
|
156
|
+
const queryVec = Float32Array.from([1, 0, 0, 0]);
|
|
157
|
+
const strongSemVec = Float32Array.from([0.95, 0.05, 0, 0]); // cosine ≈ 0.998
|
|
158
|
+
const store = makeStore({
|
|
159
|
+
ftsHits: [],
|
|
160
|
+
embeddedChunks: [{ id: 1, commitHash: "abc", kind: "subject", text: "semantically related", vec: strongSemVec }],
|
|
161
|
+
});
|
|
162
|
+
const results = await search("paraphrased query", {
|
|
163
|
+
store,
|
|
164
|
+
embedder: fakeEmbedder(queryVec),
|
|
165
|
+
});
|
|
166
|
+
expect(results.length).toBeGreaterThan(0);
|
|
167
|
+
});
|
|
168
|
+
it('confidenceFloor: "off" disables the filter (returns whatever fusion produced)', async () => {
|
|
169
|
+
const queryVec = Float32Array.from([1, 0, 0, 0]);
|
|
170
|
+
const orthogonalVec = Float32Array.from([0, 1, 0, 0]);
|
|
171
|
+
const store = makeStore({
|
|
172
|
+
ftsHits: [],
|
|
173
|
+
embeddedChunks: [{ id: 1, commitHash: "abc", kind: "subject", text: "x", vec: orthogonalVec }],
|
|
174
|
+
});
|
|
175
|
+
const results = await search("xyzzyplugh", {
|
|
176
|
+
store,
|
|
177
|
+
embedder: fakeEmbedder(queryVec),
|
|
178
|
+
confidenceFloor: "off",
|
|
179
|
+
});
|
|
180
|
+
// With floor disabled, semantic still produces a (low-quality) hit
|
|
181
|
+
expect(results.length).toBeGreaterThan(0);
|
|
182
|
+
});
|
|
183
|
+
it("DEFAULT_CONFIDENCE thresholds are exposed for tuning", () => {
|
|
184
|
+
expect(DEFAULT_CONFIDENCE.minFtsHits).toBe(1);
|
|
185
|
+
expect(DEFAULT_CONFIDENCE.minSemCosine).toBe(0.4);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
105
188
|
//# sourceMappingURL=search.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.test.js","sourceRoot":"","sources":["../../src/retrieve/search.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"search.test.js","sourceRoot":"","sources":["../../src/retrieve/search.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM synthesis layer — turns retrieval results into a paragraph answer.
|
|
3
|
+
*
|
|
4
|
+
* Without this, `mneme ask` returns a list of commits and lets the user piece
|
|
5
|
+
* together the answer themselves. With this, the answer reads like a brief
|
|
6
|
+
* from a senior dev: "X exists because Y; here are the commits that prove it."
|
|
7
|
+
*
|
|
8
|
+
* The synthesizer is *grounded* — it MUST cite commit hashes and MUST refuse
|
|
9
|
+
* to invent claims not present in the retrieved evidence. The prompt enforces
|
|
10
|
+
* this; the temperature is low (0.2). On hallucination risk, we err on the
|
|
11
|
+
* side of saying "the evidence is mixed" rather than fabricating a thesis.
|
|
12
|
+
*
|
|
13
|
+
* Falls back to an extractive answer (template over top-3 commits) when no
|
|
14
|
+
* LLM is reachable. The extractive path is itself good enough for most
|
|
15
|
+
* keyword queries — the LLM is the polish.
|
|
16
|
+
*/
|
|
17
|
+
import type { SearchResult } from "../types.js";
|
|
18
|
+
import type { ConfidenceLabel } from "./search.js";
|
|
19
|
+
/**
|
|
20
|
+
* Subset of EnricherProvider needed here. We avoid a hard dependency on
|
|
21
|
+
* @mneme-ai/embeddings to keep core dep-free; callers pass an enricher in.
|
|
22
|
+
*/
|
|
23
|
+
export interface SynthesisEnricher {
|
|
24
|
+
readonly name: string;
|
|
25
|
+
enrich(input: {
|
|
26
|
+
system: string;
|
|
27
|
+
user: string;
|
|
28
|
+
temperature?: number;
|
|
29
|
+
maxTokens?: number;
|
|
30
|
+
}): Promise<{
|
|
31
|
+
text: string;
|
|
32
|
+
}>;
|
|
33
|
+
}
|
|
34
|
+
export interface SynthesizedAnswer {
|
|
35
|
+
/** The paragraph the user reads at the top of `mneme ask` output. */
|
|
36
|
+
answer: string;
|
|
37
|
+
/** Provenance: where the answer came from. */
|
|
38
|
+
source: "llm" | "extractive" | "no-context";
|
|
39
|
+
/** The same confidence label that was passed in (echoed for caller convenience). */
|
|
40
|
+
confidence: ConfidenceLabel;
|
|
41
|
+
/** Hashes of the commits the answer is grounded in. */
|
|
42
|
+
evidenceCommitHashes: string[];
|
|
43
|
+
/** Per-call latency for telemetry / "how slow is this?" calibration. */
|
|
44
|
+
durationMs: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Synthesize an answer from retrieval results.
|
|
48
|
+
*
|
|
49
|
+
* @param question — the user's original query (already classified as 'specific'
|
|
50
|
+
* or 'lookup' by `classifyIntent`).
|
|
51
|
+
* @param results — top-K SearchResult, ordered by score.
|
|
52
|
+
* @param confidence — pre-computed by `classifyConfidence(results)`.
|
|
53
|
+
* @param enricher — optional. If undefined or fails, we fall back to extractive.
|
|
54
|
+
* @param maxResults — how many top results to ground the answer in (default 5).
|
|
55
|
+
*/
|
|
56
|
+
export declare function synthesize(question: string, results: SearchResult[], confidence: ConfidenceLabel, enricher?: SynthesisEnricher, maxResults?: number): Promise<SynthesizedAnswer>;
|
|
57
|
+
//# sourceMappingURL=synthesize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"synthesize.d.ts","sourceRoot":"","sources":["../../src/retrieve/synthesize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,KAAK,EAAE;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,MAAM,EAAE,KAAK,GAAG,YAAY,GAAG,YAAY,CAAC;IAC5C,oFAAoF;IACpF,UAAU,EAAE,eAAe,CAAC;IAC5B,uDAAuD;IACvD,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;CACpB;AAaD;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,YAAY,EAAE,EACvB,UAAU,EAAE,eAAe,EAC3B,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,UAAU,SAAI,GACb,OAAO,CAAC,iBAAiB,CAAC,CAoD5B"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM synthesis layer — turns retrieval results into a paragraph answer.
|
|
3
|
+
*
|
|
4
|
+
* Without this, `mneme ask` returns a list of commits and lets the user piece
|
|
5
|
+
* together the answer themselves. With this, the answer reads like a brief
|
|
6
|
+
* from a senior dev: "X exists because Y; here are the commits that prove it."
|
|
7
|
+
*
|
|
8
|
+
* The synthesizer is *grounded* — it MUST cite commit hashes and MUST refuse
|
|
9
|
+
* to invent claims not present in the retrieved evidence. The prompt enforces
|
|
10
|
+
* this; the temperature is low (0.2). On hallucination risk, we err on the
|
|
11
|
+
* side of saying "the evidence is mixed" rather than fabricating a thesis.
|
|
12
|
+
*
|
|
13
|
+
* Falls back to an extractive answer (template over top-3 commits) when no
|
|
14
|
+
* LLM is reachable. The extractive path is itself good enough for most
|
|
15
|
+
* keyword queries — the LLM is the polish.
|
|
16
|
+
*/
|
|
17
|
+
const SYNTH_SYSTEM_PROMPT = [
|
|
18
|
+
"You are a senior engineer answering a question about a code repository's history.",
|
|
19
|
+
"You are given a question and the top-K commits retrieved from the repo's git history.",
|
|
20
|
+
"Write a 2-4 sentence answer that:",
|
|
21
|
+
" • Cites at least one commit hash (short form, 7 chars) using inline backticks.",
|
|
22
|
+
" • Says only what the evidence supports — never invent a claim.",
|
|
23
|
+
" • If the evidence is thin or contradicts itself, say so plainly.",
|
|
24
|
+
" • Avoids filler ('the codebase shows', 'as we can see') and lists.",
|
|
25
|
+
"Output the answer as plain text, no markdown lists, no headings, no preamble.",
|
|
26
|
+
].join("\n");
|
|
27
|
+
/**
|
|
28
|
+
* Synthesize an answer from retrieval results.
|
|
29
|
+
*
|
|
30
|
+
* @param question — the user's original query (already classified as 'specific'
|
|
31
|
+
* or 'lookup' by `classifyIntent`).
|
|
32
|
+
* @param results — top-K SearchResult, ordered by score.
|
|
33
|
+
* @param confidence — pre-computed by `classifyConfidence(results)`.
|
|
34
|
+
* @param enricher — optional. If undefined or fails, we fall back to extractive.
|
|
35
|
+
* @param maxResults — how many top results to ground the answer in (default 5).
|
|
36
|
+
*/
|
|
37
|
+
export async function synthesize(question, results, confidence, enricher, maxResults = 5) {
|
|
38
|
+
const t0 = Date.now();
|
|
39
|
+
if (confidence === "none" || results.length === 0) {
|
|
40
|
+
return {
|
|
41
|
+
answer: noContextAnswer(question),
|
|
42
|
+
source: "no-context",
|
|
43
|
+
confidence,
|
|
44
|
+
evidenceCommitHashes: [],
|
|
45
|
+
durationMs: Date.now() - t0,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const top = results.slice(0, maxResults);
|
|
49
|
+
const evidenceHashes = top.map((r) => r.commit.hash);
|
|
50
|
+
if (!enricher) {
|
|
51
|
+
return {
|
|
52
|
+
answer: extractiveAnswer(question, top, confidence),
|
|
53
|
+
source: "extractive",
|
|
54
|
+
confidence,
|
|
55
|
+
evidenceCommitHashes: evidenceHashes,
|
|
56
|
+
durationMs: Date.now() - t0,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const userPrompt = buildSynthesisPrompt(question, top, confidence);
|
|
61
|
+
const out = await enricher.enrich({
|
|
62
|
+
system: SYNTH_SYSTEM_PROMPT,
|
|
63
|
+
user: userPrompt,
|
|
64
|
+
temperature: 0.2,
|
|
65
|
+
maxTokens: 250,
|
|
66
|
+
});
|
|
67
|
+
const cleaned = cleanLlmOutput(out.text);
|
|
68
|
+
return {
|
|
69
|
+
answer: cleaned || extractiveAnswer(question, top, confidence),
|
|
70
|
+
source: cleaned ? "llm" : "extractive",
|
|
71
|
+
confidence,
|
|
72
|
+
evidenceCommitHashes: evidenceHashes,
|
|
73
|
+
durationMs: Date.now() - t0,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// LLM failed (timeout, model not pulled, network) — extractive fallback.
|
|
78
|
+
return {
|
|
79
|
+
answer: extractiveAnswer(question, top, confidence),
|
|
80
|
+
source: "extractive",
|
|
81
|
+
confidence,
|
|
82
|
+
evidenceCommitHashes: evidenceHashes,
|
|
83
|
+
durationMs: Date.now() - t0,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Build the user-side prompt: question + numbered evidence with hashes,
|
|
89
|
+
* dates, authors, subjects, and a snippet of the body. Compact enough to fit
|
|
90
|
+
* comfortably in any modern LLM's context.
|
|
91
|
+
*/
|
|
92
|
+
function buildSynthesisPrompt(question, results, confidence) {
|
|
93
|
+
const lines = [];
|
|
94
|
+
lines.push(`Question: ${question}`);
|
|
95
|
+
lines.push("");
|
|
96
|
+
lines.push(`Retrieval confidence: ${confidence}`);
|
|
97
|
+
if (confidence === "low") {
|
|
98
|
+
lines.push("(The evidence is thin — say so honestly if the answer cannot be supported.)");
|
|
99
|
+
}
|
|
100
|
+
lines.push("");
|
|
101
|
+
lines.push("Top commits:");
|
|
102
|
+
results.forEach((r, i) => {
|
|
103
|
+
const hash = r.commit.shortHash || r.commit.hash.slice(0, 7);
|
|
104
|
+
const date = r.commit.authorDate.slice(0, 10);
|
|
105
|
+
const author = r.commit.authorName;
|
|
106
|
+
const subject = r.commit.subject;
|
|
107
|
+
const body = (r.commit.body || "").split("\n").slice(0, 3).join(" ").trim().slice(0, 280);
|
|
108
|
+
lines.push(`${i + 1}. \`${hash}\` (${date}, ${author}): ${subject}`);
|
|
109
|
+
if (body)
|
|
110
|
+
lines.push(` ${body}`);
|
|
111
|
+
});
|
|
112
|
+
lines.push("");
|
|
113
|
+
lines.push("Answer:");
|
|
114
|
+
return lines.join("\n");
|
|
115
|
+
}
|
|
116
|
+
/** Strip common LLM artifacts: leading "Answer:", surrounding quotes, trailing meta. */
|
|
117
|
+
function cleanLlmOutput(text) {
|
|
118
|
+
let t = text.trim();
|
|
119
|
+
// Drop a leading "Answer:" if the model echoed it back.
|
|
120
|
+
t = t.replace(/^answer\s*:\s*/i, "");
|
|
121
|
+
// Drop wrapping quotes if any.
|
|
122
|
+
if ((t.startsWith('"') && t.endsWith('"')) || (t.startsWith("'") && t.endsWith("'"))) {
|
|
123
|
+
t = t.slice(1, -1).trim();
|
|
124
|
+
}
|
|
125
|
+
// Strip trailing "Sources: ..." or "Citations: ..." (we already render evidence below).
|
|
126
|
+
t = t.replace(/\n+\s*(sources?|citations?|references?)\s*:[\s\S]*$/i, "");
|
|
127
|
+
return t.trim();
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Template-based answer — used when no LLM is configured or LLM fails.
|
|
131
|
+
* Honest and short: "Top X matches: PR #N (author, date) — subject."
|
|
132
|
+
*/
|
|
133
|
+
function extractiveAnswer(_question, results, confidence) {
|
|
134
|
+
const top = results.slice(0, 3);
|
|
135
|
+
const parts = top.map((r) => {
|
|
136
|
+
const hash = r.commit.shortHash || r.commit.hash.slice(0, 7);
|
|
137
|
+
const date = r.commit.authorDate.slice(0, 10);
|
|
138
|
+
return `\`${hash}\` (${date}): ${r.commit.subject}`;
|
|
139
|
+
});
|
|
140
|
+
const prefix = confidence === "high"
|
|
141
|
+
? "The most likely answer is in"
|
|
142
|
+
: confidence === "medium"
|
|
143
|
+
? "Possibly relevant"
|
|
144
|
+
: "Weakly relevant — please verify";
|
|
145
|
+
if (top.length === 1)
|
|
146
|
+
return `${prefix} ${parts[0]}.`;
|
|
147
|
+
if (top.length === 2)
|
|
148
|
+
return `${prefix} ${parts[0]} and ${parts[1]}.`;
|
|
149
|
+
return `${prefix} ${parts[0]}; also ${parts[1]} and ${parts[2]}.`;
|
|
150
|
+
}
|
|
151
|
+
/** Honest "no context found" — never returns a result. */
|
|
152
|
+
function noContextAnswer(_question) {
|
|
153
|
+
return [
|
|
154
|
+
"No strong context found for this question.",
|
|
155
|
+
"Either the answer lives outside git (a Slack thread, a design doc, an in-person decision)",
|
|
156
|
+
"or the question is too general for a memory-layer tool — try something specific like",
|
|
157
|
+
'"why does <function or pattern> exist?" or "when did we change <module>?"',
|
|
158
|
+
].join(" ");
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=synthesize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"synthesize.js","sourceRoot":"","sources":["../../src/retrieve/synthesize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"synthesize.test.d.ts","sourceRoot":"","sources":["../../src/retrieve/synthesize.test.ts"],"names":[],"mappings":""}
|