@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
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { redact, containsSecret, mergeHits } from "./redact.js";
|
|
3
|
+
describe("redact — high-confidence rules", () => {
|
|
4
|
+
it("strips AWS access key id (AKIA prefix)", () => {
|
|
5
|
+
const r = redact("token=AKIAIOSFODNN7EXAMPLE in config");
|
|
6
|
+
expect(r.text).toContain("<REDACTED:aws-access-key-id>");
|
|
7
|
+
expect(r.text).not.toContain("AKIAIOSFODNN7EXAMPLE");
|
|
8
|
+
expect(r.hits["aws-access-key-id"]).toBe(1);
|
|
9
|
+
});
|
|
10
|
+
it("strips AWS access key id (ASIA prefix — temp credentials)", () => {
|
|
11
|
+
const r = redact("ASIAIOSFODNN7EXAMPLE");
|
|
12
|
+
expect(r.text).toContain("<REDACTED:aws-access-key-id>");
|
|
13
|
+
});
|
|
14
|
+
it("strips GitHub PAT (ghp_ classic)", () => {
|
|
15
|
+
const tok = "ghp" + "_" + "A".repeat(36);
|
|
16
|
+
const r = redact(`Cloning with ${tok} failed`);
|
|
17
|
+
expect(r.text).toContain("<REDACTED:github-pat>");
|
|
18
|
+
expect(r.hits["github-pat"]).toBe(1);
|
|
19
|
+
});
|
|
20
|
+
it("strips GitHub fine-grained PAT (github_pat_ prefix)", () => {
|
|
21
|
+
const tok = "github" + "_pat_" + "A".repeat(85);
|
|
22
|
+
const r = redact(`token=${tok}`);
|
|
23
|
+
expect(r.text).not.toContain(tok);
|
|
24
|
+
});
|
|
25
|
+
it("strips Stripe live secret keys", () => {
|
|
26
|
+
// Constructed at runtime so the source file does not contain a literal
|
|
27
|
+
// string that GitHub's secret scanner flags as a real Stripe key.
|
|
28
|
+
const stripeKey = "sk" + "_live_" + "A".repeat(24);
|
|
29
|
+
const r = redact(`Stripe key: ${stripeKey}`);
|
|
30
|
+
expect(r.text).toContain("<REDACTED:stripe-key>");
|
|
31
|
+
});
|
|
32
|
+
it("strips OpenAI keys (sk- prefix)", () => {
|
|
33
|
+
const openaiKey = "sk" + "-" + "A".repeat(20);
|
|
34
|
+
const r = redact(`OPENAI_API_KEY=${openaiKey}`);
|
|
35
|
+
expect(r.text).toContain("<REDACTED:openai-key>");
|
|
36
|
+
});
|
|
37
|
+
it("strips Anthropic keys (sk-ant- prefix)", () => {
|
|
38
|
+
const anthropicKey = "sk" + "-ant-" + "A".repeat(32);
|
|
39
|
+
const r = redact(`ANTHROPIC_API_KEY=${anthropicKey}`);
|
|
40
|
+
expect(r.text).toContain("<REDACTED:anthropic-key>");
|
|
41
|
+
});
|
|
42
|
+
it("strips Slack tokens (xox prefix)", () => {
|
|
43
|
+
// Constructed at runtime to avoid GitHub secret-scanner false positives.
|
|
44
|
+
const slackToken = "xox" + "b-" + "1234567890-1234567890-" + "A".repeat(14);
|
|
45
|
+
const r = redact(`Slack: ${slackToken}`);
|
|
46
|
+
expect(r.text).toContain("<REDACTED:slack-token>");
|
|
47
|
+
});
|
|
48
|
+
it("strips Google API keys (AIza prefix)", () => {
|
|
49
|
+
// Real format: AIza + exactly 35 chars of [0-9A-Za-z_-]
|
|
50
|
+
const key = "AIza" + "B".repeat(35);
|
|
51
|
+
const r = redact(`GOOGLE_API_KEY=${key}`);
|
|
52
|
+
expect(r.text).toContain("<REDACTED:google-api-key>");
|
|
53
|
+
});
|
|
54
|
+
it("strips npm tokens", () => {
|
|
55
|
+
const r = redact("NPM_TOKEN=npm_" + "x".repeat(36));
|
|
56
|
+
expect(r.text).toContain("<REDACTED:npm-token>");
|
|
57
|
+
});
|
|
58
|
+
it("strips JWTs (3 base64url segments)", () => {
|
|
59
|
+
const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" +
|
|
60
|
+
".eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4ifQ" +
|
|
61
|
+
".SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
|
62
|
+
const r = redact(`auth: ${jwt}`);
|
|
63
|
+
expect(r.text).toContain("<REDACTED:jwt>");
|
|
64
|
+
});
|
|
65
|
+
it("strips multiline PEM private keys", () => {
|
|
66
|
+
const pem = [
|
|
67
|
+
"-----BEGIN RSA PRIVATE KEY-----",
|
|
68
|
+
"MIIBOQIBAAJAVhx/mmddhM4...",
|
|
69
|
+
"-----END RSA PRIVATE KEY-----",
|
|
70
|
+
].join("\n");
|
|
71
|
+
const r = redact(`config:\n${pem}\nend`);
|
|
72
|
+
expect(r.text).toContain("<REDACTED:pem-private-key>");
|
|
73
|
+
expect(r.text).not.toContain("MIIBOQIBAAJAVhx");
|
|
74
|
+
});
|
|
75
|
+
it("strips Bearer tokens", () => {
|
|
76
|
+
const r = redact("Authorization: Bearer abcDEFghiJKLmnoPQRstuVWXyz0123456");
|
|
77
|
+
expect(r.text).toContain("<REDACTED:bearer-token>");
|
|
78
|
+
});
|
|
79
|
+
it("counts multiple hits per rule", () => {
|
|
80
|
+
const r = redact("k1=AKIAIOSFODNN7EXAMPLE k2=AKIAIOSFODNN7EXAMPLF");
|
|
81
|
+
expect(r.hits["aws-access-key-id"]).toBe(2);
|
|
82
|
+
});
|
|
83
|
+
it("counts hits across rules", () => {
|
|
84
|
+
const r = redact("aws=AKIAIOSFODNN7EXAMPLE gh=ghp_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
|
|
85
|
+
expect(r.hits["aws-access-key-id"]).toBe(1);
|
|
86
|
+
expect(r.hits["github-pat"]).toBe(1);
|
|
87
|
+
});
|
|
88
|
+
it("leaves clean text unchanged", () => {
|
|
89
|
+
const text = "Fix Stripe webhook crash on amount=BigInt overflow";
|
|
90
|
+
const r = redact(text);
|
|
91
|
+
expect(r.text).toBe(text);
|
|
92
|
+
expect(r.hits).toEqual({});
|
|
93
|
+
});
|
|
94
|
+
it("handles empty input", () => {
|
|
95
|
+
expect(redact("").text).toBe("");
|
|
96
|
+
expect(redact("").hits).toEqual({});
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
describe("redact — aggressive mode", () => {
|
|
100
|
+
it('catches generic password=... when { aggressive: true }', () => {
|
|
101
|
+
const r = redact("DB_PASSWORD=hunter2-very-long-password", { aggressive: true });
|
|
102
|
+
expect(r.text).toContain("<REDACTED:password-assignment>");
|
|
103
|
+
});
|
|
104
|
+
it("does NOT catch generic password=... by default (false-positive avoidance)", () => {
|
|
105
|
+
const r = redact("DB_PASSWORD=hunter2-very-long-password");
|
|
106
|
+
expect(r.text).toContain("hunter2-very-long-password");
|
|
107
|
+
});
|
|
108
|
+
it("catches long hex blobs only when aggressive", () => {
|
|
109
|
+
const hex = "a".repeat(64);
|
|
110
|
+
expect(redact(hex).text).toBe(hex);
|
|
111
|
+
expect(redact(hex, { aggressive: true }).text).toContain("<REDACTED:hex-blob>");
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
describe("redact — custom rules and overrides", () => {
|
|
115
|
+
it("applies extraRules from caller", () => {
|
|
116
|
+
const r = redact("internal-secret=XYZZY", {
|
|
117
|
+
extraRules: [{ name: "internal-secret", pattern: /\binternal-secret=[A-Z]+/g }],
|
|
118
|
+
});
|
|
119
|
+
expect(r.text).toContain("<REDACTED:internal-secret>");
|
|
120
|
+
});
|
|
121
|
+
it("respects custom replacement string when built-in is disabled", () => {
|
|
122
|
+
const r = redact("k=AKIAIOSFODNN7EXAMPLE", {
|
|
123
|
+
disableRules: ["aws-access-key-id"],
|
|
124
|
+
extraRules: [{ name: "aws", pattern: /\bAKIA[0-9A-Z]{16}\b/g, replacement: "[hidden]" }],
|
|
125
|
+
});
|
|
126
|
+
expect(r.text).toBe("k=[hidden]");
|
|
127
|
+
});
|
|
128
|
+
it("disableRules removes a built-in rule", () => {
|
|
129
|
+
const r = redact("AKIAIOSFODNN7EXAMPLE", { disableRules: ["aws-access-key-id"] });
|
|
130
|
+
expect(r.text).toContain("AKIAIOSFODNN7EXAMPLE");
|
|
131
|
+
expect(r.hits["aws-access-key-id"]).toBeUndefined();
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
describe("containsSecret", () => {
|
|
135
|
+
it("returns true when any rule matches", () => {
|
|
136
|
+
expect(containsSecret("ghp_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")).toBe(true);
|
|
137
|
+
});
|
|
138
|
+
it("returns false on clean text", () => {
|
|
139
|
+
expect(containsSecret("Fix Stripe webhook crash on BigInt amounts")).toBe(false);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
describe("mergeHits", () => {
|
|
143
|
+
it("sums per-rule counters across two reports", () => {
|
|
144
|
+
const merged = mergeHits({ "aws": 2, "gh": 1 }, { "aws": 3, "stripe": 5 });
|
|
145
|
+
expect(merged).toEqual({ aws: 5, gh: 1, stripe: 5 });
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
//# sourceMappingURL=redact.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.test.js","sourceRoot":"","sources":["../../src/util/redact.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-calibrator — picks the search config that maximizes hit rate on the
|
|
3
|
+
* accumulated feedback set.
|
|
4
|
+
*
|
|
5
|
+
* Approach: small-grid search over (semanticWeight × minSemCosine × rrfK).
|
|
6
|
+
* Re-runs every query in the feedback set with each config and counts how
|
|
7
|
+
* many of the originally-helpful results stay in top-K. The config with the
|
|
8
|
+
* highest count wins.
|
|
9
|
+
*
|
|
10
|
+
* Pragmatically:
|
|
11
|
+
* - Grid is intentionally small (3 × 3 × 2 = 18 configs) to fit in seconds.
|
|
12
|
+
* - We only consider feedback where was_helpful = 1 (positive examples).
|
|
13
|
+
* - We require ≥ 10 positive examples before calibrating; below that, the
|
|
14
|
+
* defaults are statistically stronger than any tuned value.
|
|
15
|
+
*
|
|
16
|
+
* The output is written to wisdom_calibration so search() can read the
|
|
17
|
+
* latest calibrated values without re-running the calibrator.
|
|
18
|
+
*/
|
|
19
|
+
import type { MnemeStore } from "../store/sqlite.js";
|
|
20
|
+
import type { EmbeddingProvider } from "../types.js";
|
|
21
|
+
import { type CalibrationConfig, type CalibrationRow } from "./types.js";
|
|
22
|
+
export interface CalibrationResult {
|
|
23
|
+
/** The winning config. If no calibration ran, equals DEFAULT_CALIBRATION. */
|
|
24
|
+
config: CalibrationConfig;
|
|
25
|
+
/** Score on the feedback set: fraction of helpful results re-found in top-3. */
|
|
26
|
+
hitRate: number;
|
|
27
|
+
/** Number of positive examples evaluated (gating threshold: 10). */
|
|
28
|
+
positiveExamples: number;
|
|
29
|
+
/** Whether the calibrator actually ran or returned defaults. */
|
|
30
|
+
calibrated: boolean;
|
|
31
|
+
/** The full grid result, sorted desc by hitRate. */
|
|
32
|
+
grid: Array<CalibrationConfig & {
|
|
33
|
+
hitRate: number;
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
export declare function calibrate(store: MnemeStore, embedder?: EmbeddingProvider): Promise<CalibrationResult>;
|
|
37
|
+
export declare function persistCalibration(store: MnemeStore, key: string, config: CalibrationConfig & {
|
|
38
|
+
hitRate: number;
|
|
39
|
+
}, metricName: string, notes?: string): void;
|
|
40
|
+
/** Read back the current calibration. Returns DEFAULT_CALIBRATION if no row. */
|
|
41
|
+
export declare function readCalibration(store: MnemeStore, key?: string): CalibrationConfig;
|
|
42
|
+
export declare function listCalibrations(store: MnemeStore): CalibrationRow[];
|
|
43
|
+
//# sourceMappingURL=calibrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calibrator.d.ts","sourceRoot":"","sources":["../../src/wisdom/calibrator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EAEpB,MAAM,YAAY,CAAC;AAWpB,MAAM,WAAW,iBAAiB;IAChC,6EAA6E;IAC7E,MAAM,EAAE,iBAAiB,CAAC;IAC1B,gFAAgF;IAChF,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,gBAAgB,EAAE,MAAM,CAAC;IACzB,gEAAgE;IAChE,UAAU,EAAE,OAAO,CAAC;IACpB,oDAAoD;IACpD,IAAI,EAAE,KAAK,CAAC,iBAAiB,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACtD;AAID,wBAAsB,SAAS,CAC7B,KAAK,EAAE,UAAU,EACjB,QAAQ,CAAC,EAAE,iBAAiB,GAC3B,OAAO,CAAC,iBAAiB,CAAC,CAmD5B;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,iBAAiB,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,EAC/C,UAAU,EAAE,MAAM,EAClB,KAAK,CAAC,EAAE,MAAM,GACb,IAAI,CAmBN;AAED,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,SAAW,GAAG,iBAAiB,CAiBpF;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,cAAc,EAAE,CAsBpE"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-calibrator — picks the search config that maximizes hit rate on the
|
|
3
|
+
* accumulated feedback set.
|
|
4
|
+
*
|
|
5
|
+
* Approach: small-grid search over (semanticWeight × minSemCosine × rrfK).
|
|
6
|
+
* Re-runs every query in the feedback set with each config and counts how
|
|
7
|
+
* many of the originally-helpful results stay in top-K. The config with the
|
|
8
|
+
* highest count wins.
|
|
9
|
+
*
|
|
10
|
+
* Pragmatically:
|
|
11
|
+
* - Grid is intentionally small (3 × 3 × 2 = 18 configs) to fit in seconds.
|
|
12
|
+
* - We only consider feedback where was_helpful = 1 (positive examples).
|
|
13
|
+
* - We require ≥ 10 positive examples before calibrating; below that, the
|
|
14
|
+
* defaults are statistically stronger than any tuned value.
|
|
15
|
+
*
|
|
16
|
+
* The output is written to wisdom_calibration so search() can read the
|
|
17
|
+
* latest calibrated values without re-running the calibrator.
|
|
18
|
+
*/
|
|
19
|
+
import { search } from "../retrieve/search.js";
|
|
20
|
+
import { listFeedback } from "./feedback.js";
|
|
21
|
+
import { DEFAULT_CALIBRATION, } from "./types.js";
|
|
22
|
+
const SEARCH_GRID = [];
|
|
23
|
+
for (const semanticWeight of [0.4, 0.65, 0.85]) {
|
|
24
|
+
for (const minSemCosine of [0.3, 0.4, 0.5]) {
|
|
25
|
+
for (const rrfK of [60, 30]) {
|
|
26
|
+
SEARCH_GRID.push({ semanticWeight, minSemCosine, rrfK });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const MIN_POSITIVE_EXAMPLES = 10;
|
|
31
|
+
export async function calibrate(store, embedder) {
|
|
32
|
+
const positives = listFeedback(store, { limit: 500 }).filter((f) => f.wasHelpful === 1 && f.resultHashes.length > 0);
|
|
33
|
+
if (positives.length < MIN_POSITIVE_EXAMPLES) {
|
|
34
|
+
return {
|
|
35
|
+
config: DEFAULT_CALIBRATION,
|
|
36
|
+
hitRate: 0,
|
|
37
|
+
positiveExamples: positives.length,
|
|
38
|
+
calibrated: false,
|
|
39
|
+
grid: [],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const results = [];
|
|
43
|
+
for (const config of SEARCH_GRID) {
|
|
44
|
+
let hits = 0;
|
|
45
|
+
for (const ex of positives) {
|
|
46
|
+
// The "ground truth" is the top-3 hashes from the original query —
|
|
47
|
+
// we re-run with the candidate config and check overlap.
|
|
48
|
+
const groundTruth = new Set(ex.resultHashes.slice(0, 3));
|
|
49
|
+
const re = await search(ex.query, {
|
|
50
|
+
store,
|
|
51
|
+
embedder,
|
|
52
|
+
topK: 3,
|
|
53
|
+
semanticWeight: config.semanticWeight,
|
|
54
|
+
confidenceFloor: { minFtsHits: 1, minSemCosine: config.minSemCosine },
|
|
55
|
+
});
|
|
56
|
+
const reHashes = new Set(re.map((r) => r.commit.hash));
|
|
57
|
+
// Count this query as a "hit" if at least one of the original useful
|
|
58
|
+
// results is still surfaced.
|
|
59
|
+
const overlap = [...groundTruth].some((h) => reHashes.has(h));
|
|
60
|
+
if (overlap)
|
|
61
|
+
hits += 1;
|
|
62
|
+
}
|
|
63
|
+
results.push({ ...config, hitRate: hits / positives.length });
|
|
64
|
+
}
|
|
65
|
+
results.sort((a, b) => b.hitRate - a.hitRate);
|
|
66
|
+
const best = results[0];
|
|
67
|
+
persistCalibration(store, "search", best, "hit_rate");
|
|
68
|
+
return {
|
|
69
|
+
config: { semanticWeight: best.semanticWeight, minSemCosine: best.minSemCosine, rrfK: best.rrfK },
|
|
70
|
+
hitRate: best.hitRate,
|
|
71
|
+
positiveExamples: positives.length,
|
|
72
|
+
calibrated: true,
|
|
73
|
+
grid: results,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export function persistCalibration(store, key, config, metricName, notes) {
|
|
77
|
+
store.db
|
|
78
|
+
.prepare(`INSERT OR REPLACE INTO wisdom_calibration
|
|
79
|
+
(key, value, metric_value, metric_name, calibrated_at, notes)
|
|
80
|
+
VALUES (?, ?, ?, ?, ?, ?)`)
|
|
81
|
+
.run(key, JSON.stringify({
|
|
82
|
+
semanticWeight: config.semanticWeight,
|
|
83
|
+
minSemCosine: config.minSemCosine,
|
|
84
|
+
rrfK: config.rrfK,
|
|
85
|
+
}), config.hitRate, metricName, new Date().toISOString(), notes ?? null);
|
|
86
|
+
}
|
|
87
|
+
/** Read back the current calibration. Returns DEFAULT_CALIBRATION if no row. */
|
|
88
|
+
export function readCalibration(store, key = "search") {
|
|
89
|
+
const row = store.db
|
|
90
|
+
.prepare(`SELECT value FROM wisdom_calibration WHERE key = ? ORDER BY calibrated_at DESC LIMIT 1`)
|
|
91
|
+
.get(key);
|
|
92
|
+
if (!row)
|
|
93
|
+
return DEFAULT_CALIBRATION;
|
|
94
|
+
try {
|
|
95
|
+
const parsed = JSON.parse(row.value);
|
|
96
|
+
return {
|
|
97
|
+
semanticWeight: parsed.semanticWeight ?? DEFAULT_CALIBRATION.semanticWeight,
|
|
98
|
+
minSemCosine: parsed.minSemCosine ?? DEFAULT_CALIBRATION.minSemCosine,
|
|
99
|
+
rrfK: parsed.rrfK ?? DEFAULT_CALIBRATION.rrfK,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return DEFAULT_CALIBRATION;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export function listCalibrations(store) {
|
|
107
|
+
const rows = store.db
|
|
108
|
+
.prepare(`SELECT key, value, metric_value, metric_name, calibrated_at, notes
|
|
109
|
+
FROM wisdom_calibration ORDER BY calibrated_at DESC`)
|
|
110
|
+
.all();
|
|
111
|
+
return rows.map((r) => ({
|
|
112
|
+
key: r.key,
|
|
113
|
+
value: JSON.parse(r.value),
|
|
114
|
+
metricValue: r.metric_value ?? undefined,
|
|
115
|
+
metricName: r.metric_name ?? undefined,
|
|
116
|
+
calibratedAt: r.calibrated_at,
|
|
117
|
+
notes: r.notes ?? undefined,
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=calibrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calibrator.js","sourceRoot":"","sources":["../../src/wisdom/calibrator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAE/C,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAGL,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,GAAwB,EAAE,CAAC;AAC5C,KAAK,MAAM,cAAc,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;IAC/C,KAAK,MAAM,YAAY,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;QAC3C,KAAK,MAAM,IAAI,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;YAC5B,WAAW,CAAC,IAAI,CAAC,EAAE,cAAc,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;AACH,CAAC;AAeD,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAEjC,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,KAAiB,EACjB,QAA4B;IAE5B,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,CAC1D,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CACvD,CAAC;IAEF,IAAI,SAAS,CAAC,MAAM,GAAG,qBAAqB,EAAE,CAAC;QAC7C,OAAO;YACL,MAAM,EAAE,mBAAmB;YAC3B,OAAO,EAAE,CAAC;YACV,gBAAgB,EAAE,SAAS,CAAC,MAAM;YAClC,UAAU,EAAE,KAAK;YACjB,IAAI,EAAE,EAAE;SACT,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAmD,EAAE,CAAC;IAEnE,KAAK,MAAM,MAAM,IAAI,WAAW,EAAE,CAAC;QACjC,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;YAC3B,mEAAmE;YACnE,yDAAyD;YACzD,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACzD,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE;gBAChC,KAAK;gBACL,QAAQ;gBACR,IAAI,EAAE,CAAC;gBACP,cAAc,EAAE,MAAM,CAAC,cAAc;gBACrC,eAAe,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE;aACtE,CAAC,CAAC;YACH,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;YACvD,qEAAqE;YACrE,6BAA6B;YAC7B,MAAM,OAAO,GAAG,CAAC,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9D,IAAI,OAAO;gBAAE,IAAI,IAAI,CAAC,CAAC;QACzB,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAE,CAAC;IAEzB,kBAAkB,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;IAEtD,OAAO;QACL,MAAM,EAAE,EAAE,cAAc,EAAE,IAAI,CAAC,cAAc,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;QACjG,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,gBAAgB,EAAE,SAAS,CAAC,MAAM;QAClC,UAAU,EAAE,IAAI;QAChB,IAAI,EAAE,OAAO;KACd,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,KAAiB,EACjB,GAAW,EACX,MAA+C,EAC/C,UAAkB,EAClB,KAAc;IAEd,KAAK,CAAC,EAAE;SACL,OAAO,CACN;;iCAE2B,CAC5B;SACA,GAAG,CACF,GAAG,EACH,IAAI,CAAC,SAAS,CAAC;QACb,cAAc,EAAE,MAAM,CAAC,cAAc;QACrC,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,IAAI,EAAE,MAAM,CAAC,IAAI;KAClB,CAAC,EACF,MAAM,CAAC,OAAO,EACd,UAAU,EACV,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EACxB,KAAK,IAAI,IAAI,CACd,CAAC;AACN,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,eAAe,CAAC,KAAiB,EAAE,GAAG,GAAG,QAAQ;IAC/D,MAAM,GAAG,GAAG,KAAK,CAAC,EAAE;SACjB,OAAO,CACN,wFAAwF,CACzF;SACA,GAAG,CAAC,GAAG,CAAkC,CAAC;IAC7C,IAAI,CAAC,GAAG;QAAE,OAAO,mBAAmB,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAA+B,CAAC;QACnE,OAAO;YACL,cAAc,EAAE,MAAM,CAAC,cAAc,IAAI,mBAAmB,CAAC,cAAc;YAC3E,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,mBAAmB,CAAC,YAAY;YACrE,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,mBAAmB,CAAC,IAAI;SAC9C,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,mBAAmB,CAAC;IAC7B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAiB;IAChD,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE;SAClB,OAAO,CACN;2DACqD,CACtD;SACA,GAAG,EAOJ,CAAC;IACH,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtB,GAAG,EAAE,CAAC,CAAC,GAAG;QACV,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAY;QACrC,WAAW,EAAE,CAAC,CAAC,YAAY,IAAI,SAAS;QACxC,UAAU,EAAE,CAAC,CAAC,WAAW,IAAI,SAAS;QACtC,YAAY,EAAE,CAAC,CAAC,aAAa;QAC7B,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,SAAS;KAC5B,CAAC,CAAC,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feedback collector — every `mneme ask` writes one row.
|
|
3
|
+
*
|
|
4
|
+
* was_helpful starts NULL and is filled by:
|
|
5
|
+
* - explicit: `mneme feedback <id> up|down`
|
|
6
|
+
* - implicit-revisit: the user ran `mneme why <commit>` on a returned commit
|
|
7
|
+
* within 5 minutes — strong positive signal.
|
|
8
|
+
* - implicit-reuse: the user did not re-ask a slightly-different version of
|
|
9
|
+
* the same query within 60s — weak positive signal.
|
|
10
|
+
*
|
|
11
|
+
* Why these heuristics: they require zero UX work from the user. The
|
|
12
|
+
* explicit path exists for power users; the implicit paths cover the 95%.
|
|
13
|
+
*/
|
|
14
|
+
import type { MnemeStore } from "../store/sqlite.js";
|
|
15
|
+
import type { FeedbackRow } from "./types.js";
|
|
16
|
+
export interface RecordFeedbackInput {
|
|
17
|
+
query: string;
|
|
18
|
+
resultHashes: string[];
|
|
19
|
+
topScore?: number;
|
|
20
|
+
semanticWeight?: number;
|
|
21
|
+
minSemCosine?: number;
|
|
22
|
+
rrfK?: number;
|
|
23
|
+
}
|
|
24
|
+
/** Insert a pending feedback row. Returns the id so callers can update it later. */
|
|
25
|
+
export declare function recordQuery(store: MnemeStore, input: RecordFeedbackInput): string;
|
|
26
|
+
/** Set was_helpful for a feedback row. */
|
|
27
|
+
export declare function setHelpful(store: MnemeStore, id: string, wasHelpful: boolean, source?: FeedbackRow["source"]): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Implicit positive signal: the user looked at one of the returned commits
|
|
30
|
+
* via `mneme why`. We mark the most recent matching pending row as helpful.
|
|
31
|
+
*/
|
|
32
|
+
export declare function recordImplicitRevisit(store: MnemeStore, commitHash: string): number;
|
|
33
|
+
export declare function listFeedback(store: MnemeStore, opts?: {
|
|
34
|
+
since?: string;
|
|
35
|
+
limit?: number;
|
|
36
|
+
}): FeedbackRow[];
|
|
37
|
+
export interface FeedbackSummary {
|
|
38
|
+
total: number;
|
|
39
|
+
helpful: number;
|
|
40
|
+
unhelpful: number;
|
|
41
|
+
pending: number;
|
|
42
|
+
hitRate: number;
|
|
43
|
+
}
|
|
44
|
+
export declare function summarizeFeedback(store: MnemeStore, since?: string): FeedbackSummary;
|
|
45
|
+
//# sourceMappingURL=feedback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedback.d.ts","sourceRoot":"","sources":["../../src/wisdom/feedback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,oFAAoF;AACpF,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,mBAAmB,GAAG,MAAM,CAqBjF;AAED,0CAA0C;AAC1C,wBAAgB,UAAU,CACxB,KAAK,EAAE,UAAU,EACjB,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,OAAO,EACnB,MAAM,GAAE,WAAW,CAAC,QAAQ,CAAc,GACzC,OAAO,CAKT;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAsBnF;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,WAAW,EAAE,CAwC5G;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,eAAe,CA0BpF"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feedback collector — every `mneme ask` writes one row.
|
|
3
|
+
*
|
|
4
|
+
* was_helpful starts NULL and is filled by:
|
|
5
|
+
* - explicit: `mneme feedback <id> up|down`
|
|
6
|
+
* - implicit-revisit: the user ran `mneme why <commit>` on a returned commit
|
|
7
|
+
* within 5 minutes — strong positive signal.
|
|
8
|
+
* - implicit-reuse: the user did not re-ask a slightly-different version of
|
|
9
|
+
* the same query within 60s — weak positive signal.
|
|
10
|
+
*
|
|
11
|
+
* Why these heuristics: they require zero UX work from the user. The
|
|
12
|
+
* explicit path exists for power users; the implicit paths cover the 95%.
|
|
13
|
+
*/
|
|
14
|
+
import { randomUUID } from "node:crypto";
|
|
15
|
+
/** Insert a pending feedback row. Returns the id so callers can update it later. */
|
|
16
|
+
export function recordQuery(store, input) {
|
|
17
|
+
const id = randomUUID();
|
|
18
|
+
const now = new Date().toISOString();
|
|
19
|
+
store.db
|
|
20
|
+
.prepare(`INSERT INTO wisdom_feedback
|
|
21
|
+
(id, query, result_hashes, top_score, was_helpful, source,
|
|
22
|
+
semantic_weight, min_sem_cosine, rrf_k, created_at)
|
|
23
|
+
VALUES (?, ?, ?, ?, NULL, 'pending', ?, ?, ?, ?)`)
|
|
24
|
+
.run(id, input.query, JSON.stringify(input.resultHashes), input.topScore ?? null, input.semanticWeight ?? null, input.minSemCosine ?? null, input.rrfK ?? null, now);
|
|
25
|
+
return id;
|
|
26
|
+
}
|
|
27
|
+
/** Set was_helpful for a feedback row. */
|
|
28
|
+
export function setHelpful(store, id, wasHelpful, source = "explicit") {
|
|
29
|
+
const result = store.db
|
|
30
|
+
.prepare(`UPDATE wisdom_feedback SET was_helpful = ?, source = ? WHERE id = ?`)
|
|
31
|
+
.run(wasHelpful ? 1 : 0, source, id);
|
|
32
|
+
return result.changes > 0;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Implicit positive signal: the user looked at one of the returned commits
|
|
36
|
+
* via `mneme why`. We mark the most recent matching pending row as helpful.
|
|
37
|
+
*/
|
|
38
|
+
export function recordImplicitRevisit(store, commitHash) {
|
|
39
|
+
const recent = store.db
|
|
40
|
+
.prepare(`SELECT id, result_hashes FROM wisdom_feedback
|
|
41
|
+
WHERE was_helpful IS NULL
|
|
42
|
+
AND created_at >= datetime('now', '-5 minutes')
|
|
43
|
+
ORDER BY created_at DESC
|
|
44
|
+
LIMIT 20`)
|
|
45
|
+
.all();
|
|
46
|
+
let updated = 0;
|
|
47
|
+
for (const row of recent) {
|
|
48
|
+
try {
|
|
49
|
+
const hashes = JSON.parse(row.result_hashes);
|
|
50
|
+
if (hashes.includes(commitHash)) {
|
|
51
|
+
if (setHelpful(store, row.id, true, "implicit-revisit"))
|
|
52
|
+
updated += 1;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// ignore corrupt JSON; the row is just an inert breadcrumb in that case
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return updated;
|
|
60
|
+
}
|
|
61
|
+
export function listFeedback(store, opts = {}) {
|
|
62
|
+
const where = [];
|
|
63
|
+
const params = [];
|
|
64
|
+
if (opts.since) {
|
|
65
|
+
where.push("created_at >= ?");
|
|
66
|
+
params.push(opts.since);
|
|
67
|
+
}
|
|
68
|
+
const sql = `
|
|
69
|
+
SELECT id, query, result_hashes, top_score, was_helpful, source,
|
|
70
|
+
semantic_weight, min_sem_cosine, rrf_k, created_at
|
|
71
|
+
FROM wisdom_feedback
|
|
72
|
+
${where.length ? "WHERE " + where.join(" AND ") : ""}
|
|
73
|
+
ORDER BY created_at DESC
|
|
74
|
+
LIMIT ?
|
|
75
|
+
`;
|
|
76
|
+
params.push(opts.limit ?? 1000);
|
|
77
|
+
const rows = store.db.prepare(sql).all(...params);
|
|
78
|
+
return rows.map((r) => ({
|
|
79
|
+
id: r.id,
|
|
80
|
+
query: r.query,
|
|
81
|
+
resultHashes: JSON.parse(r.result_hashes),
|
|
82
|
+
topScore: r.top_score ?? undefined,
|
|
83
|
+
wasHelpful: r.was_helpful === 1 ? 1 : r.was_helpful === 0 ? 0 : undefined,
|
|
84
|
+
source: r.source,
|
|
85
|
+
semanticWeight: r.semantic_weight ?? undefined,
|
|
86
|
+
minSemCosine: r.min_sem_cosine ?? undefined,
|
|
87
|
+
rrfK: r.rrf_k ?? undefined,
|
|
88
|
+
createdAt: r.created_at,
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
91
|
+
export function summarizeFeedback(store, since) {
|
|
92
|
+
const params = [];
|
|
93
|
+
let where = "";
|
|
94
|
+
if (since) {
|
|
95
|
+
where = "WHERE created_at >= ?";
|
|
96
|
+
params.push(since);
|
|
97
|
+
}
|
|
98
|
+
const row = store.db
|
|
99
|
+
.prepare(`SELECT
|
|
100
|
+
COUNT(*) AS total,
|
|
101
|
+
SUM(CASE WHEN was_helpful = 1 THEN 1 ELSE 0 END) AS helpful,
|
|
102
|
+
SUM(CASE WHEN was_helpful = 0 THEN 1 ELSE 0 END) AS unhelpful,
|
|
103
|
+
SUM(CASE WHEN was_helpful IS NULL THEN 1 ELSE 0 END) AS pending
|
|
104
|
+
FROM wisdom_feedback ${where}`)
|
|
105
|
+
.get(...params);
|
|
106
|
+
const decided = row.helpful + row.unhelpful;
|
|
107
|
+
const hitRate = decided === 0 ? 0 : row.helpful / decided;
|
|
108
|
+
return {
|
|
109
|
+
total: row.total,
|
|
110
|
+
helpful: row.helpful,
|
|
111
|
+
unhelpful: row.unhelpful,
|
|
112
|
+
pending: row.pending,
|
|
113
|
+
hitRate,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=feedback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedback.js","sourceRoot":"","sources":["../../src/wisdom/feedback.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wisdom Mutant Engine — public surface.
|
|
3
|
+
*
|
|
4
|
+
* The engine has three appendable artifacts (feedback, calibrations,
|
|
5
|
+
* eval-runs) and three operations on them: collect, calibrate, self-eval.
|
|
6
|
+
*
|
|
7
|
+
* Inspired by AlphaGo Zero's loop — observe, adapt, measure, repeat —
|
|
8
|
+
* minus the reinforcement learning. We are not training a model. We are
|
|
9
|
+
* tuning a small set of search knobs against the user's own feedback
|
|
10
|
+
* over time. The point is the *loop*, not the algorithm.
|
|
11
|
+
*/
|
|
12
|
+
export * from "./types.js";
|
|
13
|
+
export * from "./feedback.js";
|
|
14
|
+
export * from "./calibrator.js";
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/wisdom/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wisdom Mutant Engine — public surface.
|
|
3
|
+
*
|
|
4
|
+
* The engine has three appendable artifacts (feedback, calibrations,
|
|
5
|
+
* eval-runs) and three operations on them: collect, calibrate, self-eval.
|
|
6
|
+
*
|
|
7
|
+
* Inspired by AlphaGo Zero's loop — observe, adapt, measure, repeat —
|
|
8
|
+
* minus the reinforcement learning. We are not training a model. We are
|
|
9
|
+
* tuning a small set of search knobs against the user's own feedback
|
|
10
|
+
* over time. The point is the *loop*, not the algorithm.
|
|
11
|
+
*/
|
|
12
|
+
export * from "./types.js";
|
|
13
|
+
export * from "./feedback.js";
|
|
14
|
+
export * from "./calibrator.js";
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/wisdom/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wisdom Mutant Engine — types.
|
|
3
|
+
*
|
|
4
|
+
* The "self-improving memory" subsystem. Three loops:
|
|
5
|
+
* 1. Feedback collector — every query records what was returned.
|
|
6
|
+
* 2. Auto-calibrator — periodically tunes search knobs against the
|
|
7
|
+
* feedback set, picks the config that maximizes
|
|
8
|
+
* hit rate.
|
|
9
|
+
* 3. Self-eval — runs eval set against current config and writes
|
|
10
|
+
* a row to wisdom_eval_run. Detects regressions.
|
|
11
|
+
*
|
|
12
|
+
* The point: `mneme adapt` recommendations and the search thresholds in
|
|
13
|
+
* `mneme ask` are not hardcoded. They are derived from this engine's history.
|
|
14
|
+
*/
|
|
15
|
+
export interface FeedbackRow {
|
|
16
|
+
id: string;
|
|
17
|
+
query: string;
|
|
18
|
+
/** JSON array of commit hashes returned at query time. */
|
|
19
|
+
resultHashes: string[];
|
|
20
|
+
/** Top RRF score at query time (a confidence proxy). */
|
|
21
|
+
topScore?: number;
|
|
22
|
+
/**
|
|
23
|
+
* 1 = the user found the result useful (or an implicit signal said so).
|
|
24
|
+
* 0 = explicitly marked not useful.
|
|
25
|
+
* undefined = unknown — usually because no feedback has been collected yet.
|
|
26
|
+
*/
|
|
27
|
+
wasHelpful?: 1 | 0;
|
|
28
|
+
/** How was_helpful was set. */
|
|
29
|
+
source: "explicit" | "implicit-reuse" | "implicit-revisit" | "pending";
|
|
30
|
+
/** Search config in effect when the query ran. */
|
|
31
|
+
semanticWeight?: number;
|
|
32
|
+
minSemCosine?: number;
|
|
33
|
+
rrfK?: number;
|
|
34
|
+
createdAt: string;
|
|
35
|
+
}
|
|
36
|
+
export interface CalibrationRow {
|
|
37
|
+
key: string;
|
|
38
|
+
/** Stored as JSON; parse with JSON.parse. */
|
|
39
|
+
value: unknown;
|
|
40
|
+
/** The metric this calibration achieved on the feedback set. */
|
|
41
|
+
metricValue?: number;
|
|
42
|
+
metricName?: string;
|
|
43
|
+
calibratedAt: string;
|
|
44
|
+
notes?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface EvalRunRow {
|
|
47
|
+
id: number;
|
|
48
|
+
ranAt: string;
|
|
49
|
+
variant: string;
|
|
50
|
+
recallAt3: number;
|
|
51
|
+
mrr: number;
|
|
52
|
+
ndcgAt10?: number;
|
|
53
|
+
hitRate: number;
|
|
54
|
+
semanticWeight?: number;
|
|
55
|
+
minSemCosine?: number;
|
|
56
|
+
rrfK?: number;
|
|
57
|
+
numQueries: number;
|
|
58
|
+
notes?: string;
|
|
59
|
+
}
|
|
60
|
+
/** Search-time knobs the calibrator can tune. */
|
|
61
|
+
export interface CalibrationConfig {
|
|
62
|
+
semanticWeight: number;
|
|
63
|
+
minSemCosine: number;
|
|
64
|
+
rrfK: number;
|
|
65
|
+
}
|
|
66
|
+
export declare const DEFAULT_CALIBRATION: CalibrationConfig;
|
|
67
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/wisdom/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACnB,+BAA+B;IAC/B,MAAM,EAAE,UAAU,GAAG,gBAAgB,GAAG,kBAAkB,GAAG,SAAS,CAAC;IACvE,kDAAkD;IAClD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,6CAA6C;IAC7C,KAAK,EAAE,OAAO,CAAC;IACf,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,iDAAiD;AACjD,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,eAAO,MAAM,mBAAmB,EAAE,iBAIjC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wisdom Mutant Engine — types.
|
|
3
|
+
*
|
|
4
|
+
* The "self-improving memory" subsystem. Three loops:
|
|
5
|
+
* 1. Feedback collector — every query records what was returned.
|
|
6
|
+
* 2. Auto-calibrator — periodically tunes search knobs against the
|
|
7
|
+
* feedback set, picks the config that maximizes
|
|
8
|
+
* hit rate.
|
|
9
|
+
* 3. Self-eval — runs eval set against current config and writes
|
|
10
|
+
* a row to wisdom_eval_run. Detects regressions.
|
|
11
|
+
*
|
|
12
|
+
* The point: `mneme adapt` recommendations and the search thresholds in
|
|
13
|
+
* `mneme ask` are not hardcoded. They are derived from this engine's history.
|
|
14
|
+
*/
|
|
15
|
+
export const DEFAULT_CALIBRATION = {
|
|
16
|
+
semanticWeight: 0.65,
|
|
17
|
+
minSemCosine: 0.4,
|
|
18
|
+
rrfK: 60,
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/wisdom/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAyDH,MAAM,CAAC,MAAM,mBAAmB,GAAsB;IACpD,cAAc,EAAE,IAAI;IACpB,YAAY,EAAE,GAAG;IACjB,IAAI,EAAE,EAAE;CACT,CAAC"}
|