lancedb-opencode-pro 0.1.3 → 0.1.5
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/README.md +59 -9
- package/dist/config.js +8 -0
- package/dist/index.js +20 -0
- package/dist/store.d.ts +4 -0
- package/dist/store.js +115 -5
- package/dist/types.d.ts +19 -0
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -41,7 +41,11 @@ If you already use other plugins, keep them and append `"lancedb-opencode-pro"`.
|
|
|
41
41
|
"mode": "hybrid",
|
|
42
42
|
"vectorWeight": 0.7,
|
|
43
43
|
"bm25Weight": 0.3,
|
|
44
|
-
"minScore": 0.2
|
|
44
|
+
"minScore": 0.2,
|
|
45
|
+
"rrfK": 60,
|
|
46
|
+
"recencyBoost": true,
|
|
47
|
+
"recencyHalfLifeHours": 72,
|
|
48
|
+
"importanceWeight": 0.4
|
|
45
49
|
},
|
|
46
50
|
"includeGlobalScope": true,
|
|
47
51
|
"minCaptureChars": 80,
|
|
@@ -173,7 +177,11 @@ Create `~/.config/opencode/lancedb-opencode-pro.json`:
|
|
|
173
177
|
"mode": "hybrid",
|
|
174
178
|
"vectorWeight": 0.7,
|
|
175
179
|
"bm25Weight": 0.3,
|
|
176
|
-
"minScore": 0.2
|
|
180
|
+
"minScore": 0.2,
|
|
181
|
+
"rrfK": 60,
|
|
182
|
+
"recencyBoost": true,
|
|
183
|
+
"recencyHalfLifeHours": 72,
|
|
184
|
+
"importanceWeight": 0.4
|
|
177
185
|
},
|
|
178
186
|
"includeGlobalScope": true,
|
|
179
187
|
"minCaptureChars": 80,
|
|
@@ -216,6 +224,10 @@ Supported environment variables:
|
|
|
216
224
|
- `LANCEDB_OPENCODE_PRO_VECTOR_WEIGHT`
|
|
217
225
|
- `LANCEDB_OPENCODE_PRO_BM25_WEIGHT`
|
|
218
226
|
- `LANCEDB_OPENCODE_PRO_MIN_SCORE`
|
|
227
|
+
- `LANCEDB_OPENCODE_PRO_RRF_K`
|
|
228
|
+
- `LANCEDB_OPENCODE_PRO_RECENCY_BOOST`
|
|
229
|
+
- `LANCEDB_OPENCODE_PRO_RECENCY_HALF_LIFE_HOURS`
|
|
230
|
+
- `LANCEDB_OPENCODE_PRO_IMPORTANCE_WEIGHT`
|
|
219
231
|
- `LANCEDB_OPENCODE_PRO_INCLUDE_GLOBAL_SCOPE`
|
|
220
232
|
- `LANCEDB_OPENCODE_PRO_MIN_CAPTURE_CHARS`
|
|
221
233
|
- `LANCEDB_OPENCODE_PRO_MAX_ENTRIES_PER_SCOPE`
|
|
@@ -260,7 +272,7 @@ Example output:
|
|
|
260
272
|
```json
|
|
261
273
|
{
|
|
262
274
|
"scope": "project:my-project",
|
|
263
|
-
"totalEvents":
|
|
275
|
+
"totalEvents": 14,
|
|
264
276
|
"capture": {
|
|
265
277
|
"considered": 4,
|
|
266
278
|
"stored": 3,
|
|
@@ -271,11 +283,24 @@ Example output:
|
|
|
271
283
|
}
|
|
272
284
|
},
|
|
273
285
|
"recall": {
|
|
274
|
-
"requested":
|
|
286
|
+
"requested": 4,
|
|
275
287
|
"injected": 2,
|
|
276
|
-
"returnedResults":
|
|
277
|
-
"hitRate": 0.
|
|
278
|
-
"injectionRate": 0.
|
|
288
|
+
"returnedResults": 3,
|
|
289
|
+
"hitRate": 0.75,
|
|
290
|
+
"injectionRate": 0.5,
|
|
291
|
+
"auto": {
|
|
292
|
+
"requested": 3,
|
|
293
|
+
"injected": 2,
|
|
294
|
+
"returnedResults": 2,
|
|
295
|
+
"hitRate": 0.67,
|
|
296
|
+
"injectionRate": 0.67
|
|
297
|
+
},
|
|
298
|
+
"manual": {
|
|
299
|
+
"requested": 1,
|
|
300
|
+
"returnedResults": 1,
|
|
301
|
+
"hitRate": 1
|
|
302
|
+
},
|
|
303
|
+
"manualRescueRatio": 0.33
|
|
279
304
|
},
|
|
280
305
|
"feedback": {
|
|
281
306
|
"missing": 1,
|
|
@@ -294,10 +319,31 @@ Example output:
|
|
|
294
319
|
Key fields:
|
|
295
320
|
|
|
296
321
|
- `capture.successRate`: how often a considered candidate was stored.
|
|
297
|
-
- `recall.hitRate`: how often
|
|
322
|
+
- `recall.hitRate`: blended rate across auto and manual recall — how often any recall request returned at least one result.
|
|
323
|
+
- `recall.auto.*`: metrics for automatic recall injected into the system prompt during `experimental.chat.system.transform`.
|
|
324
|
+
- `recall.manual.*`: metrics for user-initiated `memory_search` calls; `injected` is always false for manual searches.
|
|
325
|
+
- `recall.manualRescueRatio`: `manual.requested / auto.requested` — a proxy for how often users still need to search manually despite automatic recall.
|
|
298
326
|
- `feedback.falsePositiveRate`: wrong-memory reports divided by stored memories.
|
|
299
327
|
- `feedback.falseNegativeRate`: missing-memory reports relative to capture attempts.
|
|
300
328
|
|
|
329
|
+
### Interpreting Low-Feedback Results
|
|
330
|
+
|
|
331
|
+
In real OpenCode usage, auto-capture and recall happen in the background, so explicit `memory_feedback_*` events are often sparse.
|
|
332
|
+
|
|
333
|
+
- Treat `capture.*` and `recall.*` as system-health metrics: they show whether the memory pipeline is running.
|
|
334
|
+
- Treat `recall.auto.*` and `recall.manual.*` separately: auto metrics reflect pipeline health; manual metrics reflect whether users still need to rescue context manually.
|
|
335
|
+
- Treat `recall.manualRescueRatio` as a proxy for manual rescue rate: a high ratio suggests automatic recall is not surfacing relevant context on its own.
|
|
336
|
+
- Treat repeated-context reduction, clarification burden, manual memory rescue, correction signals, and sampled audits as product-value signals: they show whether memory actually helped the user.
|
|
337
|
+
- Treat `feedback.* = 0` as insufficient evidence, not proof that memory quality is good.
|
|
338
|
+
- Treat a high `recall.hitRate` or `recall.injectionRate` as recall availability only; those values do not prove usefulness by themselves.
|
|
339
|
+
|
|
340
|
+
Recommended review order in low-feedback environments:
|
|
341
|
+
|
|
342
|
+
1. Check `capture.successRate`, `capture.skipReasons`, `recall.hitRate`, and `recall.injectionRate` for operational health.
|
|
343
|
+
2. Review whether users repeated background context less often or needed fewer clarification turns.
|
|
344
|
+
3. Check whether users still needed manual rescue through `memory_search` or issued correction-like responses.
|
|
345
|
+
4. Run a bounded audit of recalled memories or skipped captures before concluding the system is helping.
|
|
346
|
+
|
|
301
347
|
## OpenAI Embedding Configuration
|
|
302
348
|
|
|
303
349
|
Default behavior stays on Ollama. To use OpenAI embeddings, set `embedding.provider` to `openai` and provide API key + model.
|
|
@@ -318,7 +364,11 @@ Example sidecar:
|
|
|
318
364
|
"mode": "hybrid",
|
|
319
365
|
"vectorWeight": 0.7,
|
|
320
366
|
"bm25Weight": 0.3,
|
|
321
|
-
"minScore": 0.2
|
|
367
|
+
"minScore": 0.2,
|
|
368
|
+
"rrfK": 60,
|
|
369
|
+
"recencyBoost": true,
|
|
370
|
+
"recencyHalfLifeHours": 72,
|
|
371
|
+
"importanceWeight": 0.4
|
|
322
372
|
},
|
|
323
373
|
"includeGlobalScope": true,
|
|
324
374
|
"minCaptureChars": 80,
|
package/dist/config.js
CHANGED
|
@@ -20,6 +20,10 @@ export function resolveMemoryConfig(config, worktree) {
|
|
|
20
20
|
const weightSum = vectorWeight + bm25Weight;
|
|
21
21
|
const normalizedVectorWeight = weightSum > 0 ? vectorWeight / weightSum : 0.7;
|
|
22
22
|
const normalizedBm25Weight = weightSum > 0 ? bm25Weight / weightSum : 0.3;
|
|
23
|
+
const rrfK = Math.max(1, Math.floor(toNumber(process.env.LANCEDB_OPENCODE_PRO_RRF_K ?? retrievalRaw.rrfK, 60)));
|
|
24
|
+
const recencyBoost = toBoolean(process.env.LANCEDB_OPENCODE_PRO_RECENCY_BOOST ?? retrievalRaw.recencyBoost, true);
|
|
25
|
+
const recencyHalfLifeHours = Math.max(1, toNumber(process.env.LANCEDB_OPENCODE_PRO_RECENCY_HALF_LIFE_HOURS ?? retrievalRaw.recencyHalfLifeHours, 72));
|
|
26
|
+
const importanceWeight = clamp(toNumber(process.env.LANCEDB_OPENCODE_PRO_IMPORTANCE_WEIGHT ?? retrievalRaw.importanceWeight, 0.4), 0, 2);
|
|
23
27
|
const embeddingProvider = resolveEmbeddingProvider(firstString(process.env.LANCEDB_OPENCODE_PRO_EMBEDDING_PROVIDER, embeddingRaw.provider));
|
|
24
28
|
const embeddingModel = embeddingProvider === "openai"
|
|
25
29
|
? firstString(process.env.LANCEDB_OPENCODE_PRO_OPENAI_MODEL, process.env.LANCEDB_OPENCODE_PRO_EMBEDDING_MODEL, embeddingRaw.model)
|
|
@@ -49,6 +53,10 @@ export function resolveMemoryConfig(config, worktree) {
|
|
|
49
53
|
vectorWeight: normalizedVectorWeight,
|
|
50
54
|
bm25Weight: normalizedBm25Weight,
|
|
51
55
|
minScore: clamp(toNumber(process.env.LANCEDB_OPENCODE_PRO_MIN_SCORE ?? retrievalRaw.minScore, 0.2), 0, 1),
|
|
56
|
+
rrfK,
|
|
57
|
+
recencyBoost,
|
|
58
|
+
recencyHalfLifeHours,
|
|
59
|
+
importanceWeight,
|
|
52
60
|
},
|
|
53
61
|
includeGlobalScope: toBoolean(process.env.LANCEDB_OPENCODE_PRO_INCLUDE_GLOBAL_SCOPE ?? raw.includeGlobalScope, true),
|
|
54
62
|
minCaptureChars: Math.max(30, Math.floor(toNumber(process.env.LANCEDB_OPENCODE_PRO_MIN_CAPTURE_CHARS ?? raw.minCaptureChars, 80))),
|
package/dist/index.js
CHANGED
|
@@ -56,10 +56,15 @@ const plugin = async (input) => {
|
|
|
56
56
|
vectorWeight: state.config.retrieval.mode === "vector" ? 1 : state.config.retrieval.vectorWeight,
|
|
57
57
|
bm25Weight: state.config.retrieval.mode === "vector" ? 0 : state.config.retrieval.bm25Weight,
|
|
58
58
|
minScore: state.config.retrieval.minScore,
|
|
59
|
+
rrfK: state.config.retrieval.rrfK,
|
|
60
|
+
recencyBoost: state.config.retrieval.recencyBoost,
|
|
61
|
+
recencyHalfLifeHours: state.config.retrieval.recencyHalfLifeHours,
|
|
62
|
+
importanceWeight: state.config.retrieval.importanceWeight,
|
|
59
63
|
});
|
|
60
64
|
await state.store.putEvent({
|
|
61
65
|
id: generateId(),
|
|
62
66
|
type: "recall",
|
|
67
|
+
source: "system-transform",
|
|
63
68
|
scope: activeScope,
|
|
64
69
|
sessionID: eventInput.sessionID,
|
|
65
70
|
timestamp: Date.now(),
|
|
@@ -108,6 +113,21 @@ const plugin = async (input) => {
|
|
|
108
113
|
vectorWeight: state.config.retrieval.mode === "vector" ? 1 : state.config.retrieval.vectorWeight,
|
|
109
114
|
bm25Weight: state.config.retrieval.mode === "vector" ? 0 : state.config.retrieval.bm25Weight,
|
|
110
115
|
minScore: state.config.retrieval.minScore,
|
|
116
|
+
rrfK: state.config.retrieval.rrfK,
|
|
117
|
+
recencyBoost: state.config.retrieval.recencyBoost,
|
|
118
|
+
recencyHalfLifeHours: state.config.retrieval.recencyHalfLifeHours,
|
|
119
|
+
importanceWeight: state.config.retrieval.importanceWeight,
|
|
120
|
+
});
|
|
121
|
+
await state.store.putEvent({
|
|
122
|
+
id: generateId(),
|
|
123
|
+
type: "recall",
|
|
124
|
+
source: "manual-search",
|
|
125
|
+
scope: activeScope,
|
|
126
|
+
sessionID: context.sessionID,
|
|
127
|
+
timestamp: Date.now(),
|
|
128
|
+
resultCount: results.length,
|
|
129
|
+
injected: false,
|
|
130
|
+
metadataJson: JSON.stringify({ source: "manual-search" }),
|
|
111
131
|
});
|
|
112
132
|
if (results.length === 0)
|
|
113
133
|
return "No relevant memory found.";
|
package/dist/store.d.ts
CHANGED
|
@@ -19,6 +19,10 @@ export declare class MemoryStore {
|
|
|
19
19
|
vectorWeight: number;
|
|
20
20
|
bm25Weight: number;
|
|
21
21
|
minScore: number;
|
|
22
|
+
rrfK?: number;
|
|
23
|
+
recencyBoost?: boolean;
|
|
24
|
+
recencyHalfLifeHours?: number;
|
|
25
|
+
importanceWeight?: number;
|
|
22
26
|
}): Promise<SearchResult[]>;
|
|
23
27
|
deleteById(id: string, scopes: string[]): Promise<boolean>;
|
|
24
28
|
clearScope(scope: string): Promise<number>;
|
package/dist/store.js
CHANGED
|
@@ -59,6 +59,7 @@ export class MemoryStore {
|
|
|
59
59
|
skipReason: "",
|
|
60
60
|
resultCount: 0,
|
|
61
61
|
injected: false,
|
|
62
|
+
source: "",
|
|
62
63
|
feedbackType: "",
|
|
63
64
|
helpful: -1,
|
|
64
65
|
reason: "",
|
|
@@ -89,6 +90,7 @@ export class MemoryStore {
|
|
|
89
90
|
skipReason: event.type === "capture" ? event.skipReason ?? "" : "",
|
|
90
91
|
resultCount: event.type === "recall" ? event.resultCount : 0,
|
|
91
92
|
injected: event.type === "recall" ? event.injected : false,
|
|
93
|
+
source: event.type === "recall" ? event.source ?? "" : "",
|
|
92
94
|
feedbackType: event.type === "feedback" ? event.feedbackType : "",
|
|
93
95
|
helpful: event.type === "feedback" ? (event.helpful === undefined ? -1 : event.helpful ? 1 : 0) : -1,
|
|
94
96
|
reason: event.type === "feedback" ? event.reason ?? "" : "",
|
|
@@ -103,14 +105,50 @@ export class MemoryStore {
|
|
|
103
105
|
return [];
|
|
104
106
|
const queryTokens = tokenize(params.query);
|
|
105
107
|
const queryNorm = vecNorm(params.queryVector);
|
|
106
|
-
const
|
|
108
|
+
const useVectorChannel = params.queryVector.length > 0 && params.vectorWeight > 0;
|
|
109
|
+
const useBm25Channel = queryTokens.length > 0 && params.bm25Weight > 0;
|
|
110
|
+
const { vectorWeight, bm25Weight } = normalizeChannelWeights(useVectorChannel ? params.vectorWeight : 0, useBm25Channel ? params.bm25Weight : 0);
|
|
111
|
+
const rrfK = Math.max(1, Math.floor(params.rrfK ?? 60));
|
|
112
|
+
const recencyBoostEnabled = params.recencyBoost ?? true;
|
|
113
|
+
const recencyHalfLifeHours = Math.max(1, params.recencyHalfLifeHours ?? 72);
|
|
114
|
+
const importanceWeight = clampImportanceWeight(params.importanceWeight ?? 0.4);
|
|
115
|
+
const candidates = cached.records
|
|
107
116
|
.filter((record) => params.queryVector.length === 0 || record.vector.length === params.queryVector.length)
|
|
108
117
|
.map((record, index) => {
|
|
109
118
|
const recordNorm = cached.norms.get(record.id) ?? vecNorm(record.vector);
|
|
110
|
-
const vectorScore = fastCosine(params.queryVector, record.vector, queryNorm, recordNorm);
|
|
111
|
-
const bm25Score = bm25LikeScore(queryTokens, cached.tokenized[index], cached.idf);
|
|
112
|
-
|
|
113
|
-
|
|
119
|
+
const vectorScore = useVectorChannel ? fastCosine(params.queryVector, record.vector, queryNorm, recordNorm) : 0;
|
|
120
|
+
const bm25Score = useBm25Channel ? bm25LikeScore(queryTokens, cached.tokenized[index], cached.idf) : 0;
|
|
121
|
+
return { record, vectorScore, bm25Score };
|
|
122
|
+
});
|
|
123
|
+
if (candidates.length === 0)
|
|
124
|
+
return [];
|
|
125
|
+
const vectorRanks = useVectorChannel ? buildRankMap(candidates, (item) => item.vectorScore) : null;
|
|
126
|
+
const bm25Ranks = useBm25Channel ? buildRankMap(candidates, (item) => item.bm25Score) : null;
|
|
127
|
+
const scored = candidates
|
|
128
|
+
.map((item) => {
|
|
129
|
+
let rrfScore = 0;
|
|
130
|
+
if (vectorRanks) {
|
|
131
|
+
const rank = vectorRanks.get(item.record.id);
|
|
132
|
+
if (rank !== undefined)
|
|
133
|
+
rrfScore += vectorWeight / (rrfK + rank);
|
|
134
|
+
}
|
|
135
|
+
if (bm25Ranks) {
|
|
136
|
+
const rank = bm25Ranks.get(item.record.id);
|
|
137
|
+
if (rank !== undefined)
|
|
138
|
+
rrfScore += bm25Weight / (rrfK + rank);
|
|
139
|
+
}
|
|
140
|
+
rrfScore *= rrfK + 1;
|
|
141
|
+
const recencyFactor = recencyBoostEnabled
|
|
142
|
+
? computeRecencyMultiplier(item.record.timestamp, recencyHalfLifeHours)
|
|
143
|
+
: 1;
|
|
144
|
+
const importanceFactor = 1 + importanceWeight * clampImportance(item.record.importance);
|
|
145
|
+
const score = rrfScore * recencyFactor * importanceFactor;
|
|
146
|
+
return {
|
|
147
|
+
record: item.record,
|
|
148
|
+
score,
|
|
149
|
+
vectorScore: item.vectorScore,
|
|
150
|
+
bm25Score: item.bm25Score,
|
|
151
|
+
};
|
|
114
152
|
})
|
|
115
153
|
.filter((item) => item.score >= params.minScore)
|
|
116
154
|
.sort((a, b) => b.score - a.score)
|
|
@@ -171,6 +209,11 @@ export class MemoryStore {
|
|
|
171
209
|
let recallRequested = 0;
|
|
172
210
|
let recallInjected = 0;
|
|
173
211
|
let recallReturnedResults = 0;
|
|
212
|
+
let autoRecallRequested = 0;
|
|
213
|
+
let autoRecallInjected = 0;
|
|
214
|
+
let autoRecallReturnedResults = 0;
|
|
215
|
+
let manualRecallRequested = 0;
|
|
216
|
+
let manualRecallReturnedResults = 0;
|
|
174
217
|
let feedbackMissing = 0;
|
|
175
218
|
let feedbackWrong = 0;
|
|
176
219
|
let feedbackUsefulPositive = 0;
|
|
@@ -194,6 +237,19 @@ export class MemoryStore {
|
|
|
194
237
|
recallReturnedResults += 1;
|
|
195
238
|
if (event.injected)
|
|
196
239
|
recallInjected += 1;
|
|
240
|
+
const recallSource = event.source ?? "system-transform";
|
|
241
|
+
if (recallSource === "manual-search") {
|
|
242
|
+
manualRecallRequested += 1;
|
|
243
|
+
if (event.resultCount > 0)
|
|
244
|
+
manualRecallReturnedResults += 1;
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
autoRecallRequested += 1;
|
|
248
|
+
if (event.resultCount > 0)
|
|
249
|
+
autoRecallReturnedResults += 1;
|
|
250
|
+
if (event.injected)
|
|
251
|
+
autoRecallInjected += 1;
|
|
252
|
+
}
|
|
197
253
|
}
|
|
198
254
|
if (event.type === "feedback") {
|
|
199
255
|
if (event.feedbackType === "missing")
|
|
@@ -226,6 +282,19 @@ export class MemoryStore {
|
|
|
226
282
|
returnedResults: recallReturnedResults,
|
|
227
283
|
hitRate: recallRequested === 0 ? 0 : recallReturnedResults / recallRequested,
|
|
228
284
|
injectionRate: recallRequested === 0 ? 0 : recallInjected / recallRequested,
|
|
285
|
+
auto: {
|
|
286
|
+
requested: autoRecallRequested,
|
|
287
|
+
injected: autoRecallInjected,
|
|
288
|
+
returnedResults: autoRecallReturnedResults,
|
|
289
|
+
hitRate: autoRecallRequested === 0 ? 0 : autoRecallReturnedResults / autoRecallRequested,
|
|
290
|
+
injectionRate: autoRecallRequested === 0 ? 0 : autoRecallInjected / autoRecallRequested,
|
|
291
|
+
},
|
|
292
|
+
manual: {
|
|
293
|
+
requested: manualRecallRequested,
|
|
294
|
+
returnedResults: manualRecallReturnedResults,
|
|
295
|
+
hitRate: manualRecallRequested === 0 ? 0 : manualRecallReturnedResults / manualRecallRequested,
|
|
296
|
+
},
|
|
297
|
+
manualRescueRatio: autoRecallRequested === 0 ? 0 : manualRecallRequested / autoRecallRequested,
|
|
229
298
|
},
|
|
230
299
|
feedback: {
|
|
231
300
|
missing: feedbackMissing,
|
|
@@ -310,6 +379,7 @@ export class MemoryStore {
|
|
|
310
379
|
"skipReason",
|
|
311
380
|
"resultCount",
|
|
312
381
|
"injected",
|
|
382
|
+
"source",
|
|
313
383
|
"feedbackType",
|
|
314
384
|
"helpful",
|
|
315
385
|
"reason",
|
|
@@ -420,11 +490,14 @@ function normalizeEventRow(row) {
|
|
|
420
490
|
};
|
|
421
491
|
}
|
|
422
492
|
if (row.type === "recall") {
|
|
493
|
+
const sourceRaw = typeof row.source === "string" && row.source.length > 0 ? row.source : "system-transform";
|
|
494
|
+
const source = sourceRaw === "manual-search" ? "manual-search" : "system-transform";
|
|
423
495
|
return {
|
|
424
496
|
...base,
|
|
425
497
|
type: "recall",
|
|
426
498
|
resultCount: Number(row.resultCount ?? 0),
|
|
427
499
|
injected: Boolean(row.injected),
|
|
500
|
+
source,
|
|
428
501
|
};
|
|
429
502
|
}
|
|
430
503
|
if (row.type === "feedback") {
|
|
@@ -445,6 +518,43 @@ function normalizeEventRow(row) {
|
|
|
445
518
|
function escapeSql(value) {
|
|
446
519
|
return value.replace(/'/g, "''");
|
|
447
520
|
}
|
|
521
|
+
function buildRankMap(items, scoreOf) {
|
|
522
|
+
const ranked = [...items].sort((a, b) => scoreOf(b) - scoreOf(a));
|
|
523
|
+
const ranks = new Map();
|
|
524
|
+
for (let i = 0; i < ranked.length; i += 1) {
|
|
525
|
+
ranks.set(ranked[i].record.id, i + 1);
|
|
526
|
+
}
|
|
527
|
+
return ranks;
|
|
528
|
+
}
|
|
529
|
+
function normalizeChannelWeights(vectorWeight, bm25Weight) {
|
|
530
|
+
const sum = vectorWeight + bm25Weight;
|
|
531
|
+
if (sum <= 0) {
|
|
532
|
+
return { vectorWeight: 0.5, bm25Weight: 0.5 };
|
|
533
|
+
}
|
|
534
|
+
return {
|
|
535
|
+
vectorWeight: vectorWeight / sum,
|
|
536
|
+
bm25Weight: bm25Weight / sum,
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
function computeRecencyMultiplier(timestamp, halfLifeHours) {
|
|
540
|
+
const now = Date.now();
|
|
541
|
+
const ageMs = Math.max(0, now - timestamp);
|
|
542
|
+
const ageHours = ageMs / 3_600_000;
|
|
543
|
+
if (ageHours === 0)
|
|
544
|
+
return 1;
|
|
545
|
+
const decay = Math.pow(0.5, ageHours / halfLifeHours);
|
|
546
|
+
return 0.5 + 0.5 * decay;
|
|
547
|
+
}
|
|
548
|
+
function clampImportance(value) {
|
|
549
|
+
if (!Number.isFinite(value))
|
|
550
|
+
return 0;
|
|
551
|
+
return Math.max(0, Math.min(1, value));
|
|
552
|
+
}
|
|
553
|
+
function clampImportanceWeight(value) {
|
|
554
|
+
if (!Number.isFinite(value))
|
|
555
|
+
return 0.4;
|
|
556
|
+
return Math.max(0, Math.min(2, value));
|
|
557
|
+
}
|
|
448
558
|
function computeIdf(docs) {
|
|
449
559
|
const df = new Map();
|
|
450
560
|
for (const doc of docs) {
|
package/dist/types.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export type MemoryCategory = "preference" | "fact" | "decision" | "entity" | "ot
|
|
|
4
4
|
export type CaptureOutcome = "considered" | "skipped" | "stored";
|
|
5
5
|
export type CaptureSkipReason = "empty-buffer" | "below-min-chars" | "no-positive-signal" | "initialization-unavailable" | "embedding-unavailable" | "empty-embedding";
|
|
6
6
|
export type FeedbackType = "missing" | "wrong" | "useful";
|
|
7
|
+
export type RecallSource = "system-transform" | "manual-search";
|
|
7
8
|
export interface EmbeddingConfig {
|
|
8
9
|
provider: EmbeddingProvider;
|
|
9
10
|
model: string;
|
|
@@ -16,6 +17,10 @@ export interface RetrievalConfig {
|
|
|
16
17
|
vectorWeight: number;
|
|
17
18
|
bm25Weight: number;
|
|
18
19
|
minScore: number;
|
|
20
|
+
rrfK: number;
|
|
21
|
+
recencyBoost: boolean;
|
|
22
|
+
recencyHalfLifeHours: number;
|
|
23
|
+
importanceWeight: number;
|
|
19
24
|
}
|
|
20
25
|
export interface MemoryRuntimeConfig {
|
|
21
26
|
provider: string;
|
|
@@ -72,6 +77,7 @@ export interface RecallEvent extends MemoryEffectivenessEventBase {
|
|
|
72
77
|
type: "recall";
|
|
73
78
|
resultCount: number;
|
|
74
79
|
injected: boolean;
|
|
80
|
+
source?: RecallSource;
|
|
75
81
|
}
|
|
76
82
|
export interface FeedbackEvent extends MemoryEffectivenessEventBase {
|
|
77
83
|
type: "feedback";
|
|
@@ -97,6 +103,19 @@ export interface EffectivenessSummary {
|
|
|
97
103
|
returnedResults: number;
|
|
98
104
|
hitRate: number;
|
|
99
105
|
injectionRate: number;
|
|
106
|
+
auto: {
|
|
107
|
+
requested: number;
|
|
108
|
+
injected: number;
|
|
109
|
+
returnedResults: number;
|
|
110
|
+
hitRate: number;
|
|
111
|
+
injectionRate: number;
|
|
112
|
+
};
|
|
113
|
+
manual: {
|
|
114
|
+
requested: number;
|
|
115
|
+
returnedResults: number;
|
|
116
|
+
hitRate: number;
|
|
117
|
+
};
|
|
118
|
+
manualRescueRatio: number;
|
|
100
119
|
};
|
|
101
120
|
feedback: {
|
|
102
121
|
missing: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lancedb-opencode-pro",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "LanceDB-backed long-term memory provider for OpenCode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -63,5 +63,10 @@
|
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@types/node": "^22.13.9",
|
|
65
65
|
"typescript": "^5.8.2"
|
|
66
|
-
}
|
|
66
|
+
},
|
|
67
|
+
"directories": {
|
|
68
|
+
"doc": "docs",
|
|
69
|
+
"test": "test"
|
|
70
|
+
},
|
|
71
|
+
"author": ""
|
|
67
72
|
}
|