lancedb-opencode-pro 0.1.4 → 0.1.6
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 +24 -6
- package/dist/index.js +12 -0
- package/dist/store.d.ts +1 -0
- package/dist/store.js +54 -0
- package/dist/types.d.ts +15 -0
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -272,7 +272,7 @@ Example output:
|
|
|
272
272
|
```json
|
|
273
273
|
{
|
|
274
274
|
"scope": "project:my-project",
|
|
275
|
-
"totalEvents":
|
|
275
|
+
"totalEvents": 14,
|
|
276
276
|
"capture": {
|
|
277
277
|
"considered": 4,
|
|
278
278
|
"stored": 3,
|
|
@@ -283,11 +283,24 @@ Example output:
|
|
|
283
283
|
}
|
|
284
284
|
},
|
|
285
285
|
"recall": {
|
|
286
|
-
"requested":
|
|
286
|
+
"requested": 4,
|
|
287
287
|
"injected": 2,
|
|
288
|
-
"returnedResults":
|
|
289
|
-
"hitRate": 0.
|
|
290
|
-
"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
|
|
291
304
|
},
|
|
292
305
|
"feedback": {
|
|
293
306
|
"missing": 1,
|
|
@@ -306,7 +319,10 @@ Example output:
|
|
|
306
319
|
Key fields:
|
|
307
320
|
|
|
308
321
|
- `capture.successRate`: how often a considered candidate was stored.
|
|
309
|
-
- `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.
|
|
310
326
|
- `feedback.falsePositiveRate`: wrong-memory reports divided by stored memories.
|
|
311
327
|
- `feedback.falseNegativeRate`: missing-memory reports relative to capture attempts.
|
|
312
328
|
|
|
@@ -315,6 +331,8 @@ Key fields:
|
|
|
315
331
|
In real OpenCode usage, auto-capture and recall happen in the background, so explicit `memory_feedback_*` events are often sparse.
|
|
316
332
|
|
|
317
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.
|
|
318
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.
|
|
319
337
|
- Treat `feedback.* = 0` as insufficient evidence, not proof that memory quality is good.
|
|
320
338
|
- Treat a high `recall.hitRate` or `recall.injectionRate` as recall availability only; those values do not prove usefulness by themselves.
|
package/dist/index.js
CHANGED
|
@@ -64,6 +64,7 @@ const plugin = async (input) => {
|
|
|
64
64
|
await state.store.putEvent({
|
|
65
65
|
id: generateId(),
|
|
66
66
|
type: "recall",
|
|
67
|
+
source: "system-transform",
|
|
67
68
|
scope: activeScope,
|
|
68
69
|
sessionID: eventInput.sessionID,
|
|
69
70
|
timestamp: Date.now(),
|
|
@@ -117,6 +118,17 @@ const plugin = async (input) => {
|
|
|
117
118
|
recencyHalfLifeHours: state.config.retrieval.recencyHalfLifeHours,
|
|
118
119
|
importanceWeight: state.config.retrieval.importanceWeight,
|
|
119
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" }),
|
|
131
|
+
});
|
|
120
132
|
if (results.length === 0)
|
|
121
133
|
return "No relevant memory found.";
|
|
122
134
|
return results
|
package/dist/store.d.ts
CHANGED
package/dist/store.js
CHANGED
|
@@ -3,6 +3,7 @@ import { dirname } from "node:path";
|
|
|
3
3
|
import { tokenize } from "./utils.js";
|
|
4
4
|
const TABLE_NAME = "memories";
|
|
5
5
|
const EVENTS_TABLE_NAME = "effectiveness_events";
|
|
6
|
+
const EVENTS_SOURCE_COLUMN = "source";
|
|
6
7
|
export class MemoryStore {
|
|
7
8
|
dbPath;
|
|
8
9
|
lancedb = null;
|
|
@@ -59,6 +60,7 @@ export class MemoryStore {
|
|
|
59
60
|
skipReason: "",
|
|
60
61
|
resultCount: 0,
|
|
61
62
|
injected: false,
|
|
63
|
+
source: "",
|
|
62
64
|
feedbackType: "",
|
|
63
65
|
helpful: -1,
|
|
64
66
|
reason: "",
|
|
@@ -68,6 +70,7 @@ export class MemoryStore {
|
|
|
68
70
|
this.eventTable = await this.connection.createTable(EVENTS_TABLE_NAME, [bootstrapEvent]);
|
|
69
71
|
await this.eventTable.delete("id = '__bootstrap__'");
|
|
70
72
|
}
|
|
73
|
+
await this.ensureEventTableCompatibility();
|
|
71
74
|
await this.ensureIndexes();
|
|
72
75
|
}
|
|
73
76
|
async put(record) {
|
|
@@ -89,6 +92,7 @@ export class MemoryStore {
|
|
|
89
92
|
skipReason: event.type === "capture" ? event.skipReason ?? "" : "",
|
|
90
93
|
resultCount: event.type === "recall" ? event.resultCount : 0,
|
|
91
94
|
injected: event.type === "recall" ? event.injected : false,
|
|
95
|
+
source: event.type === "recall" ? event.source ?? "" : "",
|
|
92
96
|
feedbackType: event.type === "feedback" ? event.feedbackType : "",
|
|
93
97
|
helpful: event.type === "feedback" ? (event.helpful === undefined ? -1 : event.helpful ? 1 : 0) : -1,
|
|
94
98
|
reason: event.type === "feedback" ? event.reason ?? "" : "",
|
|
@@ -207,6 +211,11 @@ export class MemoryStore {
|
|
|
207
211
|
let recallRequested = 0;
|
|
208
212
|
let recallInjected = 0;
|
|
209
213
|
let recallReturnedResults = 0;
|
|
214
|
+
let autoRecallRequested = 0;
|
|
215
|
+
let autoRecallInjected = 0;
|
|
216
|
+
let autoRecallReturnedResults = 0;
|
|
217
|
+
let manualRecallRequested = 0;
|
|
218
|
+
let manualRecallReturnedResults = 0;
|
|
210
219
|
let feedbackMissing = 0;
|
|
211
220
|
let feedbackWrong = 0;
|
|
212
221
|
let feedbackUsefulPositive = 0;
|
|
@@ -230,6 +239,19 @@ export class MemoryStore {
|
|
|
230
239
|
recallReturnedResults += 1;
|
|
231
240
|
if (event.injected)
|
|
232
241
|
recallInjected += 1;
|
|
242
|
+
const recallSource = event.source ?? "system-transform";
|
|
243
|
+
if (recallSource === "manual-search") {
|
|
244
|
+
manualRecallRequested += 1;
|
|
245
|
+
if (event.resultCount > 0)
|
|
246
|
+
manualRecallReturnedResults += 1;
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
autoRecallRequested += 1;
|
|
250
|
+
if (event.resultCount > 0)
|
|
251
|
+
autoRecallReturnedResults += 1;
|
|
252
|
+
if (event.injected)
|
|
253
|
+
autoRecallInjected += 1;
|
|
254
|
+
}
|
|
233
255
|
}
|
|
234
256
|
if (event.type === "feedback") {
|
|
235
257
|
if (event.feedbackType === "missing")
|
|
@@ -262,6 +284,19 @@ export class MemoryStore {
|
|
|
262
284
|
returnedResults: recallReturnedResults,
|
|
263
285
|
hitRate: recallRequested === 0 ? 0 : recallReturnedResults / recallRequested,
|
|
264
286
|
injectionRate: recallRequested === 0 ? 0 : recallInjected / recallRequested,
|
|
287
|
+
auto: {
|
|
288
|
+
requested: autoRecallRequested,
|
|
289
|
+
injected: autoRecallInjected,
|
|
290
|
+
returnedResults: autoRecallReturnedResults,
|
|
291
|
+
hitRate: autoRecallRequested === 0 ? 0 : autoRecallReturnedResults / autoRecallRequested,
|
|
292
|
+
injectionRate: autoRecallRequested === 0 ? 0 : autoRecallInjected / autoRecallRequested,
|
|
293
|
+
},
|
|
294
|
+
manual: {
|
|
295
|
+
requested: manualRecallRequested,
|
|
296
|
+
returnedResults: manualRecallReturnedResults,
|
|
297
|
+
hitRate: manualRecallRequested === 0 ? 0 : manualRecallReturnedResults / manualRecallRequested,
|
|
298
|
+
},
|
|
299
|
+
manualRescueRatio: autoRecallRequested === 0 ? 0 : manualRecallRequested / autoRecallRequested,
|
|
265
300
|
},
|
|
266
301
|
feedback: {
|
|
267
302
|
missing: feedbackMissing,
|
|
@@ -346,6 +381,7 @@ export class MemoryStore {
|
|
|
346
381
|
"skipReason",
|
|
347
382
|
"resultCount",
|
|
348
383
|
"injected",
|
|
384
|
+
"source",
|
|
349
385
|
"feedbackType",
|
|
350
386
|
"helpful",
|
|
351
387
|
"reason",
|
|
@@ -411,6 +447,21 @@ export class MemoryStore {
|
|
|
411
447
|
this.indexState.ftsError = error instanceof Error ? error.message : String(error);
|
|
412
448
|
}
|
|
413
449
|
}
|
|
450
|
+
async ensureEventTableCompatibility() {
|
|
451
|
+
const table = this.requireEventTable();
|
|
452
|
+
const schema = await table.schema();
|
|
453
|
+
const hasSourceColumn = schema.fields.some((field) => field.name === EVENTS_SOURCE_COLUMN);
|
|
454
|
+
if (hasSourceColumn) {
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
try {
|
|
458
|
+
await table.addColumns([{ name: EVENTS_SOURCE_COLUMN, valueSql: "CAST(NULL AS STRING)" }]);
|
|
459
|
+
}
|
|
460
|
+
catch (error) {
|
|
461
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
462
|
+
throw new Error(`Failed to patch ${EVENTS_TABLE_NAME} schema for ${EVENTS_SOURCE_COLUMN}: ${reason}`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
414
465
|
}
|
|
415
466
|
function normalizeRow(row) {
|
|
416
467
|
const vectorRaw = row.vector;
|
|
@@ -456,11 +507,14 @@ function normalizeEventRow(row) {
|
|
|
456
507
|
};
|
|
457
508
|
}
|
|
458
509
|
if (row.type === "recall") {
|
|
510
|
+
const sourceRaw = typeof row.source === "string" && row.source.length > 0 ? row.source : "system-transform";
|
|
511
|
+
const source = sourceRaw === "manual-search" ? "manual-search" : "system-transform";
|
|
459
512
|
return {
|
|
460
513
|
...base,
|
|
461
514
|
type: "recall",
|
|
462
515
|
resultCount: Number(row.resultCount ?? 0),
|
|
463
516
|
injected: Boolean(row.injected),
|
|
517
|
+
source,
|
|
464
518
|
};
|
|
465
519
|
}
|
|
466
520
|
if (row.type === "feedback") {
|
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;
|
|
@@ -76,6 +77,7 @@ export interface RecallEvent extends MemoryEffectivenessEventBase {
|
|
|
76
77
|
type: "recall";
|
|
77
78
|
resultCount: number;
|
|
78
79
|
injected: boolean;
|
|
80
|
+
source?: RecallSource;
|
|
79
81
|
}
|
|
80
82
|
export interface FeedbackEvent extends MemoryEffectivenessEventBase {
|
|
81
83
|
type: "feedback";
|
|
@@ -101,6 +103,19 @@ export interface EffectivenessSummary {
|
|
|
101
103
|
returnedResults: number;
|
|
102
104
|
hitRate: number;
|
|
103
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;
|
|
104
119
|
};
|
|
105
120
|
feedback: {
|
|
106
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.6",
|
|
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
|
}
|