agent-working-memory 0.13.0 → 0.14.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/README.md +200 -238
- package/dist/adapters/common.d.ts +6 -0
- package/dist/adapters/common.d.ts.map +1 -1
- package/dist/adapters/common.js +457 -362
- package/dist/adapters/common.js.map +1 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +24 -8
- package/dist/api/routes.js.map +1 -1
- package/dist/core/alias-map.d.ts +16 -0
- package/dist/core/alias-map.d.ts.map +1 -0
- package/dist/core/alias-map.js +102 -0
- package/dist/core/alias-map.js.map +1 -0
- package/dist/core/embeddings.d.ts +17 -0
- package/dist/core/embeddings.d.ts.map +1 -1
- package/dist/core/embeddings.js +52 -1
- package/dist/core/embeddings.js.map +1 -1
- package/dist/core/model-cache.d.ts +28 -0
- package/dist/core/model-cache.d.ts.map +1 -0
- package/dist/core/model-cache.js +50 -0
- package/dist/core/model-cache.js.map +1 -0
- package/dist/core/query-expander.d.ts.map +1 -1
- package/dist/core/query-expander.js +2 -0
- package/dist/core/query-expander.js.map +1 -1
- package/dist/core/recall-config.d.ts +52 -0
- package/dist/core/recall-config.d.ts.map +1 -0
- package/dist/core/recall-config.js +110 -0
- package/dist/core/recall-config.js.map +1 -0
- package/dist/core/rerank-window.d.ts +61 -0
- package/dist/core/rerank-window.d.ts.map +1 -0
- package/dist/core/rerank-window.js +153 -0
- package/dist/core/rerank-window.js.map +1 -0
- package/dist/core/rerank2.d.ts +62 -0
- package/dist/core/rerank2.d.ts.map +1 -0
- package/dist/core/rerank2.js +75 -0
- package/dist/core/rerank2.js.map +1 -0
- package/dist/core/reranker.d.ts.map +1 -1
- package/dist/core/reranker.js +2 -0
- package/dist/core/reranker.js.map +1 -1
- package/dist/core/retrieval-text.d.ts +55 -0
- package/dist/core/retrieval-text.d.ts.map +1 -0
- package/dist/core/retrieval-text.js +87 -0
- package/dist/core/retrieval-text.js.map +1 -0
- package/dist/core/temporal-query.d.ts +61 -0
- package/dist/core/temporal-query.d.ts.map +1 -0
- package/dist/core/temporal-query.js +168 -0
- package/dist/core/temporal-query.js.map +1 -0
- package/dist/core/token-budget.d.ts +75 -0
- package/dist/core/token-budget.d.ts.map +1 -0
- package/dist/core/token-budget.js +136 -0
- package/dist/core/token-budget.js.map +1 -0
- package/dist/core/whoami.d.ts +11 -0
- package/dist/core/whoami.d.ts.map +1 -1
- package/dist/core/whoami.js +10 -0
- package/dist/core/whoami.js.map +1 -1
- package/dist/core/write-pipeline.d.ts.map +1 -1
- package/dist/core/write-pipeline.js +6 -3
- package/dist/core/write-pipeline.js.map +1 -1
- package/dist/engine/activation.d.ts.map +1 -1
- package/dist/engine/activation.js +135 -32
- package/dist/engine/activation.js.map +1 -1
- package/dist/hooks/prime.d.ts +77 -0
- package/dist/hooks/prime.d.ts.map +1 -0
- package/dist/hooks/prime.js +92 -0
- package/dist/hooks/prime.js.map +1 -0
- package/dist/hooks/sidecar.d.ts.map +1 -1
- package/dist/hooks/sidecar.js +39 -0
- package/dist/hooks/sidecar.js.map +1 -1
- package/dist/mcp.js +134 -102
- package/dist/mcp.js.map +1 -1
- package/dist/storage/pglite.d.ts.map +1 -1
- package/dist/storage/pglite.js +10 -2
- package/dist/storage/pglite.js.map +1 -1
- package/dist/storage/postgres.d.ts.map +1 -1
- package/dist/storage/postgres.js +10 -2
- package/dist/storage/postgres.js.map +1 -1
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +12 -2
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/types/engram.d.ts +7 -0
- package/dist/types/engram.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/adapters/common.ts +666 -567
- package/src/api/routes.ts +1015 -999
- package/src/core/alias-map.ts +97 -0
- package/src/core/embeddings.ts +172 -113
- package/src/core/model-cache.ts +51 -0
- package/src/core/query-expander.ts +2 -0
- package/src/core/recall-config.ts +115 -0
- package/src/core/rerank-window.ts +158 -0
- package/src/core/rerank2.ts +82 -0
- package/src/core/reranker.ts +2 -0
- package/src/core/retrieval-text.ts +82 -0
- package/src/core/temporal-query.ts +193 -0
- package/src/core/token-budget.ts +160 -0
- package/src/core/whoami.ts +110 -92
- package/src/core/write-pipeline.ts +6 -3
- package/src/engine/activation.ts +1568 -1468
- package/src/hooks/prime.ts +136 -0
- package/src/hooks/sidecar.ts +43 -0
- package/src/mcp.ts +1422 -1387
- package/src/storage/pglite.ts +10 -2
- package/src/storage/postgres.ts +10 -2
- package/src/storage/sqlite.ts +12 -2
- package/src/types/engram.ts +7 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-expander.js","sourceRoot":"","sources":["../../src/core/query-expander.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,sCAAsC;AACtC;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,QAAQ,EAAoC,MAAM,2BAA2B,CAAC;AACvF,OAAO,EAAE,cAAc,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"query-expander.js","sourceRoot":"","sources":["../../src/core/query-expander.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,sCAAsC;AACtC;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,QAAQ,EAAoC,MAAM,2BAA2B,CAAC;AACvF,OAAO,EAAE,cAAc,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAC3E,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD,MAAM,QAAQ,GAAG,sBAAsB,CAAC;AAExC,8BAA8B;AAE9B,IAAI,iBAAiB,GAAuC,IAAI,CAAC;AACjE,IAAI,oBAAoB,GAAgD,IAAI,CAAC;AAE7E,KAAK,UAAU,aAAa;IAC1B,IAAI,iBAAiB;QAAE,OAAO,iBAAiB,CAAC;IAChD,IAAI,oBAAoB;QAAE,OAAO,oBAAoB,CAAC;IACtD,mBAAmB,EAAE,CAAC;IACtB,oBAAoB,GAAG,QAAQ,CAAC,sBAAsB,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QAC/F,iBAAiB,GAAG,IAAmC,CAAC;QACxD,OAAO,CAAC,KAAK,CAAC,qCAAqC,QAAQ,EAAE,CAAC,CAAC;QAC/D,OAAO,iBAAiB,CAAC;IAC3B,CAAC,CAAC,CAAC;IACH,OAAO,oBAAoB,CAAC;AAC9B,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,IAAyE;IACtG,MAAM,QAAQ,GAAG,MAAM,aAAa,EAAE,CAAC;IACvC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE;QACzC,cAAc,EAAE,IAAI,CAAC,YAAY;QACjC,oBAAoB,EAAE,IAAI,CAAC,iBAAiB;KAC7C,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAE,MAAM,CAAC,CAAC,CAAS,EAAE,cAAc,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACnF,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;AAC7B,CAAC;AAED,yBAAyB,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,CAAC;AAEvD,qBAAqB;AAErB,iCAAiC;AACjC,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,OAAO,aAAa,EAAE,CAAC;AACzB,CAAC;AAED;;;GAGG;AACH,MAAM,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAC;AACjD,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAElC;;GAEG;AACH,SAAS,mBAAmB,CAAC,UAAkB;IAC7C,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,IAAI,UAAU,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,IAAI,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;IAC1E,OAAO,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,aAAqB;IACrD,MAAM,UAAU,GAAG,aAAa,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IACtD,MAAM,oBAAoB,GAAG,OAAO,CAAC,GAAG,CAAC,2BAA2B,KAAK,GAAG,CAAC;IAE7E,IAAI,oBAAoB,EAAE,CAAC;QACzB,IAAI,mBAAmB,CAAC,UAAU,CAAC;YAAE,OAAO,aAAa,CAAC;QAC1D,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC9C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,YAAY;YACZ,cAAc,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YAClC,cAAc,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YACvC,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,8HAA8H,aAAa,qBAAqB,CAAC;QAChL,MAAM,SAAS,GAAG,MAAM,cAAc,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,EAAE,iBAAiB,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3F,MAAM,UAAU,GAAG,SAAS,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC;YAClD,CAAC,CAAC,GAAG,aAAa,IAAI,SAAS,EAAE;YACjC,CAAC,CAAC,aAAa,CAAC;QAElB,IAAI,oBAAoB,EAAE,CAAC;YACzB,IAAI,cAAc,CAAC,IAAI,IAAI,qBAAqB,EAAE,CAAC;gBACjD,MAAM,SAAS,GAAG,cAAc,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;gBACrD,IAAI,SAAS,KAAK,SAAS;oBAAE,cAAc,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAChE,CAAC;YACD,cAAc,CAAC,GAAG,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QAC7C,CAAC;QAED,OAAO,UAAU,CAAC;IACpB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,aAAa,CAAC;IACvB,CAAC;AACH,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,mBAAmB;IACjC,cAAc,CAAC,KAAK,EAAE,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Effective recall configuration — self-reporting, so a measurement can PROVE
|
|
3
|
+
* which configuration produced it.
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS EXISTS
|
|
6
|
+
* ---------------
|
|
7
|
+
* On 2026-08-23 a benchmark comparison reported "no effect" for a change that
|
|
8
|
+
* demonstrably worked. The cause was not AWM: two arms shared a port, teardown
|
|
9
|
+
* left the first server alive, the second arm health-checked the survivor, and
|
|
10
|
+
* both arms measured the SAME baseline process. Identical inputs, identical
|
|
11
|
+
* outputs, a confident and completely wrong conclusion.
|
|
12
|
+
*
|
|
13
|
+
* Port hygiene fixes that instance. It does not fix the class. The class is:
|
|
14
|
+
* **nothing verified that the system measured was the system configured.**
|
|
15
|
+
* The same gap bit the D11 spreading-activation re-test, where the tracer's arm
|
|
16
|
+
* label omitted `AWM_SPREAD_INHIBIT`, so two materially different arms both
|
|
17
|
+
* printed `arm=spread` and had to be told apart by diffing output by hand.
|
|
18
|
+
*
|
|
19
|
+
* The durable fix is for the running system to state its own effective recall
|
|
20
|
+
* configuration, so a harness can assert it and fail LOUDLY instead of silently
|
|
21
|
+
* measuring the wrong thing. Any new recall flag added to RECALL_FLAGS is
|
|
22
|
+
* automatically covered by every consumer — the eval tracer's arm label, the
|
|
23
|
+
* `/health` payload, and the benchmark driver's assertion.
|
|
24
|
+
*
|
|
25
|
+
* Adding a flag here is the ONLY step required to make it visible everywhere.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Every environment flag that can change what `activate()` returns.
|
|
29
|
+
* Keep this list current — an unlisted flag is an invisible experiment.
|
|
30
|
+
*/
|
|
31
|
+
export declare const RECALL_FLAGS: readonly ["AWM_RERANK2", "AWM_RERANK2_K", "AWM_RERANK_WINDOW", "AWM_RERANK_TRUNC", "AWM_RERANK_POOL", "AWM_RERANK_TAGS", "AWM_RERANK_TAGS_LEN", "AWM_RETRIEVAL_TEXT", "AWM_ALIASES", "AWM_ALIAS_QUERY_CAP", "AWM_EMBED_MODEL", "AWM_EMBED_DIMS", "AWM_DISABLE_RERANK_SKIP", "AWM_SPREAD", "AWM_SPREAD_INJECT", "AWM_SPREAD_INHIBIT", "AWM_SPREAD_ITERS", "AWM_SPREAD_DAMPING", "AWM_SPREAD_BUDGET", "AWM_SPREAD_BOOST", "AWM_TOPN_MULT", "AWM_BROAD_EDGES", "AWM_ENTITY_FETCH", "AWM_ENTITY_INDEX_FETCH", "AWM_QUERY_BRIDGE", "AWM_AUTOTAG", "AWM_DISABLE_POOL_FILTER", "AWM_DISABLE_EXPANSION_CACHE", "AWM_DISABLE_SLIM_CACHE", "AWM_ABSTAIN_GATE_K"];
|
|
32
|
+
export type RecallFlag = (typeof RECALL_FLAGS)[number];
|
|
33
|
+
/** Only the flags actually set, in declaration order. */
|
|
34
|
+
export declare function activeRecallConfig(env?: NodeJS.ProcessEnv): Record<string, string>;
|
|
35
|
+
/**
|
|
36
|
+
* Short, stable, comparable label for the active configuration.
|
|
37
|
+
* `default` when nothing is set. Sorted so it never depends on declaration
|
|
38
|
+
* order or on how the process was launched.
|
|
39
|
+
*/
|
|
40
|
+
export declare function recallConfigFingerprint(env?: NodeJS.ProcessEnv): string;
|
|
41
|
+
/**
|
|
42
|
+
* Assert the running configuration contains the expected flag values.
|
|
43
|
+
* Returns the mismatches; empty array means the system is configured as
|
|
44
|
+
* intended. Harnesses should treat a non-empty result as fatal — it means the
|
|
45
|
+
* thing being measured is not the thing that was configured.
|
|
46
|
+
*/
|
|
47
|
+
export declare function diffRecallConfig(expected: Record<string, string>, env?: NodeJS.ProcessEnv): Array<{
|
|
48
|
+
flag: string;
|
|
49
|
+
expected: string;
|
|
50
|
+
actual: string | undefined;
|
|
51
|
+
}>;
|
|
52
|
+
//# sourceMappingURL=recall-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recall-config.d.ts","sourceRoot":"","sources":["../../src/core/recall-config.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH;;;GAGG;AACH,eAAO,MAAM,YAAY,wnBAuCf,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,yDAAyD;AACzD,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAO/F;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAKpF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC,CAOvE"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Effective recall configuration — self-reporting, so a measurement can PROVE
|
|
5
|
+
* which configuration produced it.
|
|
6
|
+
*
|
|
7
|
+
* WHY THIS EXISTS
|
|
8
|
+
* ---------------
|
|
9
|
+
* On 2026-08-23 a benchmark comparison reported "no effect" for a change that
|
|
10
|
+
* demonstrably worked. The cause was not AWM: two arms shared a port, teardown
|
|
11
|
+
* left the first server alive, the second arm health-checked the survivor, and
|
|
12
|
+
* both arms measured the SAME baseline process. Identical inputs, identical
|
|
13
|
+
* outputs, a confident and completely wrong conclusion.
|
|
14
|
+
*
|
|
15
|
+
* Port hygiene fixes that instance. It does not fix the class. The class is:
|
|
16
|
+
* **nothing verified that the system measured was the system configured.**
|
|
17
|
+
* The same gap bit the D11 spreading-activation re-test, where the tracer's arm
|
|
18
|
+
* label omitted `AWM_SPREAD_INHIBIT`, so two materially different arms both
|
|
19
|
+
* printed `arm=spread` and had to be told apart by diffing output by hand.
|
|
20
|
+
*
|
|
21
|
+
* The durable fix is for the running system to state its own effective recall
|
|
22
|
+
* configuration, so a harness can assert it and fail LOUDLY instead of silently
|
|
23
|
+
* measuring the wrong thing. Any new recall flag added to RECALL_FLAGS is
|
|
24
|
+
* automatically covered by every consumer — the eval tracer's arm label, the
|
|
25
|
+
* `/health` payload, and the benchmark driver's assertion.
|
|
26
|
+
*
|
|
27
|
+
* Adding a flag here is the ONLY step required to make it visible everywhere.
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Every environment flag that can change what `activate()` returns.
|
|
31
|
+
* Keep this list current — an unlisted flag is an invisible experiment.
|
|
32
|
+
*/
|
|
33
|
+
export const RECALL_FLAGS = [
|
|
34
|
+
// Second-stage reorder (phase 9b)
|
|
35
|
+
'AWM_RERANK2',
|
|
36
|
+
'AWM_RERANK2_K',
|
|
37
|
+
// Cross-encoder passage selection
|
|
38
|
+
'AWM_RERANK_WINDOW',
|
|
39
|
+
'AWM_RERANK_TRUNC',
|
|
40
|
+
'AWM_RERANK_POOL',
|
|
41
|
+
'AWM_RERANK_TAGS',
|
|
42
|
+
'AWM_RERANK_TAGS_LEN',
|
|
43
|
+
// Derived retrieval text (embedding side)
|
|
44
|
+
'AWM_RETRIEVAL_TEXT',
|
|
45
|
+
// Project-dialect alias expansion
|
|
46
|
+
'AWM_ALIASES',
|
|
47
|
+
'AWM_ALIAS_QUERY_CAP',
|
|
48
|
+
// Embedding model — changes every stored vector, so it belongs here
|
|
49
|
+
'AWM_EMBED_MODEL',
|
|
50
|
+
'AWM_EMBED_DIMS',
|
|
51
|
+
'AWM_DISABLE_RERANK_SKIP',
|
|
52
|
+
// Spreading activation (D11 — parked)
|
|
53
|
+
'AWM_SPREAD',
|
|
54
|
+
'AWM_SPREAD_INJECT',
|
|
55
|
+
'AWM_SPREAD_INHIBIT',
|
|
56
|
+
'AWM_SPREAD_ITERS',
|
|
57
|
+
'AWM_SPREAD_DAMPING',
|
|
58
|
+
'AWM_SPREAD_BUDGET',
|
|
59
|
+
'AWM_SPREAD_BOOST',
|
|
60
|
+
// Candidate pool / retrieval breadth
|
|
61
|
+
'AWM_TOPN_MULT',
|
|
62
|
+
'AWM_BROAD_EDGES',
|
|
63
|
+
'AWM_ENTITY_FETCH',
|
|
64
|
+
'AWM_ENTITY_INDEX_FETCH',
|
|
65
|
+
'AWM_QUERY_BRIDGE',
|
|
66
|
+
'AWM_AUTOTAG',
|
|
67
|
+
// Diagnostic escape hatches that alter ranking
|
|
68
|
+
'AWM_DISABLE_POOL_FILTER',
|
|
69
|
+
'AWM_DISABLE_EXPANSION_CACHE',
|
|
70
|
+
'AWM_DISABLE_SLIM_CACHE',
|
|
71
|
+
'AWM_ABSTAIN_GATE_K',
|
|
72
|
+
];
|
|
73
|
+
/** Only the flags actually set, in declaration order. */
|
|
74
|
+
export function activeRecallConfig(env = process.env) {
|
|
75
|
+
const out = {};
|
|
76
|
+
for (const k of RECALL_FLAGS) {
|
|
77
|
+
const v = env[k];
|
|
78
|
+
if (v !== undefined && v !== '')
|
|
79
|
+
out[k] = v;
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Short, stable, comparable label for the active configuration.
|
|
85
|
+
* `default` when nothing is set. Sorted so it never depends on declaration
|
|
86
|
+
* order or on how the process was launched.
|
|
87
|
+
*/
|
|
88
|
+
export function recallConfigFingerprint(env = process.env) {
|
|
89
|
+
const active = activeRecallConfig(env);
|
|
90
|
+
const keys = Object.keys(active).sort();
|
|
91
|
+
if (keys.length === 0)
|
|
92
|
+
return 'default';
|
|
93
|
+
return keys.map(k => `${k.replace(/^AWM_/, '').toLowerCase()}=${active[k]}`).join(',');
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Assert the running configuration contains the expected flag values.
|
|
97
|
+
* Returns the mismatches; empty array means the system is configured as
|
|
98
|
+
* intended. Harnesses should treat a non-empty result as fatal — it means the
|
|
99
|
+
* thing being measured is not the thing that was configured.
|
|
100
|
+
*/
|
|
101
|
+
export function diffRecallConfig(expected, env = process.env) {
|
|
102
|
+
const active = activeRecallConfig(env);
|
|
103
|
+
const bad = [];
|
|
104
|
+
for (const [flag, want] of Object.entries(expected)) {
|
|
105
|
+
if (active[flag] !== want)
|
|
106
|
+
bad.push({ flag, expected: want, actual: active[flag] });
|
|
107
|
+
}
|
|
108
|
+
return bad;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=recall-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recall-config.js","sourceRoot":"","sources":["../../src/core/recall-config.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,sCAAsC;AACtC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,kCAAkC;IAClC,aAAa;IACb,eAAe;IACf,kCAAkC;IAClC,mBAAmB;IACnB,kBAAkB;IAClB,iBAAiB;IACjB,iBAAiB;IACjB,qBAAqB;IACrB,0CAA0C;IAC1C,oBAAoB;IACpB,kCAAkC;IAClC,aAAa;IACb,qBAAqB;IACrB,oEAAoE;IACpE,iBAAiB;IACjB,gBAAgB;IAChB,yBAAyB;IACzB,sCAAsC;IACtC,YAAY;IACZ,mBAAmB;IACnB,oBAAoB;IACpB,kBAAkB;IAClB,oBAAoB;IACpB,mBAAmB;IACnB,kBAAkB;IAClB,qCAAqC;IACrC,eAAe;IACf,iBAAiB;IACjB,kBAAkB;IAClB,wBAAwB;IACxB,kBAAkB;IAClB,aAAa;IACb,+CAA+C;IAC/C,yBAAyB;IACzB,6BAA6B;IAC7B,wBAAwB;IACxB,oBAAoB;CACZ,CAAC;AAIX,yDAAyD;AACzD,MAAM,UAAU,kBAAkB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACrE,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;QAC7B,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QACjB,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,EAAE;YAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC1E,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IACxC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACxC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAAgC,EAChC,MAAyB,OAAO,CAAC,GAAG;IAEpC,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,GAAG,GAA0E,EAAE,CAAC;IACtF,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpD,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI;YAAE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACtF,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Passage selection for cross-encoder reranking.
|
|
3
|
+
*
|
|
4
|
+
* THE PROBLEM
|
|
5
|
+
* -----------
|
|
6
|
+
* Reranking truncates each passage to a fixed prefix (historically the first
|
|
7
|
+
* 400 chars). That truncation exists for a real reason: cross-encoders pad to
|
|
8
|
+
* the longest passage in the batch, so one 5,000-char memory in a 40-item pool
|
|
9
|
+
* drags every passage to ~512 tokens and costs 3-4x. The reranker is already
|
|
10
|
+
* ~90% of warm recall latency, so "just send everything" is not available.
|
|
11
|
+
*
|
|
12
|
+
* But a PREFIX is the wrong 400 chars. Measured on the live 29.8k store:
|
|
13
|
+
* - canonical memories median 1,965 chars, 98.7% exceed 400
|
|
14
|
+
* - the reranker cannot see 78.8% of each canonical memory's vocabulary
|
|
15
|
+
* - 99.9% of long canonical memories carry identifiers only past char 400
|
|
16
|
+
* And measured on tests/longmem-eval: moving the answer from char 150 to char
|
|
17
|
+
* 700 takes success@1 from 100% to 0%, with the gold's cross-encoder score
|
|
18
|
+
* collapsing 0.986 -> 0.000 while its BM25 score barely moves. The memory stays
|
|
19
|
+
* retrievable and stops being rankable.
|
|
20
|
+
*
|
|
21
|
+
* THE FIX
|
|
22
|
+
* -------
|
|
23
|
+
* Spend the same character budget on the window that actually contains the
|
|
24
|
+
* query's terms, instead of on whatever happens to be at the top of the memory.
|
|
25
|
+
* Cost is unchanged — same budget, same batch padding, same inference — so this
|
|
26
|
+
* buys ranking quality without buying latency.
|
|
27
|
+
*
|
|
28
|
+
* The concept line is always kept: it is short, it is the memory's title, and
|
|
29
|
+
* it is what a human wrote to summarise the thing.
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* Choose the `budget`-char window of `content` densest in query terms.
|
|
33
|
+
* Returns the head of the content when nothing matches — the old behaviour,
|
|
34
|
+
* which is the right fallback: with no query signal there is no reason to
|
|
35
|
+
* prefer any other part of the memory.
|
|
36
|
+
*/
|
|
37
|
+
export declare function densestWindow(content: string, query: string, budget: number): string;
|
|
38
|
+
/**
|
|
39
|
+
* Build the passage handed to the cross-encoder for one candidate.
|
|
40
|
+
*
|
|
41
|
+
* `mode`:
|
|
42
|
+
* - `'prefix'` (default) — legacy behaviour, the first `budget` chars.
|
|
43
|
+
* - `'query'` — the `budget`-char window densest in query terms.
|
|
44
|
+
*/
|
|
45
|
+
export declare function buildRerankPassage(concept: string, content: string, query: string, budget: number, mode: 'prefix' | 'query', tags?: string[]): string;
|
|
46
|
+
/** Whether topical tags are appended to the rerank passage. Default OFF. */
|
|
47
|
+
export declare function rerankTagsEnabled(): boolean;
|
|
48
|
+
/** Character budget for the appended tag text. */
|
|
49
|
+
export declare function rerankTagBudget(): number;
|
|
50
|
+
/**
|
|
51
|
+
* Render topical tags as plain terms for the cross-encoder.
|
|
52
|
+
* Only `topic=` / `proj=` / `project=` carry query vocabulary; date/person/
|
|
53
|
+
* ticket tags are identifiers the body usually already contains, and adding
|
|
54
|
+
* them would spend the budget without adding reachable words.
|
|
55
|
+
*/
|
|
56
|
+
export declare function rerankTagText(tags?: string[]): string;
|
|
57
|
+
/** Character budget for a rerank passage. */
|
|
58
|
+
export declare function rerankTruncation(): number;
|
|
59
|
+
/** Passage selection mode. Default `prefix` preserves shipped behaviour. */
|
|
60
|
+
export declare function rerankWindowMode(): 'prefix' | 'query';
|
|
61
|
+
//# sourceMappingURL=rerank-window.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rerank-window.d.ts","sourceRoot":"","sources":["../../src/core/rerank-window.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAaH;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAsCpF;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,QAAQ,GAAG,OAAO,EACxB,IAAI,CAAC,EAAE,MAAM,EAAE,GACd,MAAM,CAWR;AAED,4EAA4E;AAC5E,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED,kDAAkD;AAClD,wBAAgB,eAAe,IAAI,MAAM,CAGxC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAYrD;AAED,6CAA6C;AAC7C,wBAAgB,gBAAgB,IAAI,MAAM,CAGzC;AAED,4EAA4E;AAC5E,wBAAgB,gBAAgB,IAAI,QAAQ,GAAG,OAAO,CAErD"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Passage selection for cross-encoder reranking.
|
|
5
|
+
*
|
|
6
|
+
* THE PROBLEM
|
|
7
|
+
* -----------
|
|
8
|
+
* Reranking truncates each passage to a fixed prefix (historically the first
|
|
9
|
+
* 400 chars). That truncation exists for a real reason: cross-encoders pad to
|
|
10
|
+
* the longest passage in the batch, so one 5,000-char memory in a 40-item pool
|
|
11
|
+
* drags every passage to ~512 tokens and costs 3-4x. The reranker is already
|
|
12
|
+
* ~90% of warm recall latency, so "just send everything" is not available.
|
|
13
|
+
*
|
|
14
|
+
* But a PREFIX is the wrong 400 chars. Measured on the live 29.8k store:
|
|
15
|
+
* - canonical memories median 1,965 chars, 98.7% exceed 400
|
|
16
|
+
* - the reranker cannot see 78.8% of each canonical memory's vocabulary
|
|
17
|
+
* - 99.9% of long canonical memories carry identifiers only past char 400
|
|
18
|
+
* And measured on tests/longmem-eval: moving the answer from char 150 to char
|
|
19
|
+
* 700 takes success@1 from 100% to 0%, with the gold's cross-encoder score
|
|
20
|
+
* collapsing 0.986 -> 0.000 while its BM25 score barely moves. The memory stays
|
|
21
|
+
* retrievable and stops being rankable.
|
|
22
|
+
*
|
|
23
|
+
* THE FIX
|
|
24
|
+
* -------
|
|
25
|
+
* Spend the same character budget on the window that actually contains the
|
|
26
|
+
* query's terms, instead of on whatever happens to be at the top of the memory.
|
|
27
|
+
* Cost is unchanged — same budget, same batch padding, same inference — so this
|
|
28
|
+
* buys ranking quality without buying latency.
|
|
29
|
+
*
|
|
30
|
+
* The concept line is always kept: it is short, it is the memory's title, and
|
|
31
|
+
* it is what a human wrote to summarise the thing.
|
|
32
|
+
*/
|
|
33
|
+
/** Cheap tokenizer for locating query terms inside a passage. */
|
|
34
|
+
function terms(query) {
|
|
35
|
+
const raw = query.toLowerCase().match(/[a-z0-9_][a-z0-9_.\-]{1,}/g) ?? [];
|
|
36
|
+
const STOP = new Set([
|
|
37
|
+
'the', 'and', 'for', 'with', 'that', 'this', 'from', 'what', 'where', 'when',
|
|
38
|
+
'which', 'does', 'did', 'was', 'were', 'are', 'is', 'be', 'to', 'of', 'in',
|
|
39
|
+
'on', 'at', 'by', 'it', 'as', 'do', 'how', 'why', 'a', 'an', 'or',
|
|
40
|
+
]);
|
|
41
|
+
return Array.from(new Set(raw.filter(t => t.length >= 3 && !STOP.has(t))));
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Choose the `budget`-char window of `content` densest in query terms.
|
|
45
|
+
* Returns the head of the content when nothing matches — the old behaviour,
|
|
46
|
+
* which is the right fallback: with no query signal there is no reason to
|
|
47
|
+
* prefer any other part of the memory.
|
|
48
|
+
*/
|
|
49
|
+
export function densestWindow(content, query, budget) {
|
|
50
|
+
if (content.length <= budget)
|
|
51
|
+
return content;
|
|
52
|
+
const toks = terms(query);
|
|
53
|
+
if (toks.length === 0)
|
|
54
|
+
return content.slice(0, budget);
|
|
55
|
+
const lower = content.toLowerCase();
|
|
56
|
+
const hits = [];
|
|
57
|
+
for (const t of toks) {
|
|
58
|
+
let from = 0;
|
|
59
|
+
for (;;) {
|
|
60
|
+
const i = lower.indexOf(t, from);
|
|
61
|
+
if (i < 0)
|
|
62
|
+
break;
|
|
63
|
+
hits.push(i);
|
|
64
|
+
from = i + t.length;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (hits.length === 0)
|
|
68
|
+
return content.slice(0, budget);
|
|
69
|
+
hits.sort((a, b) => a - b);
|
|
70
|
+
// Slide a window anchored slightly before each hit; keep the one covering most.
|
|
71
|
+
let bestStart = 0;
|
|
72
|
+
let bestCount = -1;
|
|
73
|
+
for (let i = 0; i < hits.length; i++) {
|
|
74
|
+
const start = Math.max(0, hits[i] - Math.floor(budget / 5));
|
|
75
|
+
let count = 0;
|
|
76
|
+
for (let j = i; j < hits.length && hits[j] - start < budget; j++)
|
|
77
|
+
count++;
|
|
78
|
+
if (count > bestCount) {
|
|
79
|
+
bestCount = count;
|
|
80
|
+
bestStart = start;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Snap to a word boundary so the cross-encoder is not handed a split token.
|
|
84
|
+
let start = bestStart;
|
|
85
|
+
if (start > 0) {
|
|
86
|
+
const sp = content.indexOf(' ', start);
|
|
87
|
+
if (sp >= 0 && sp - start < 40)
|
|
88
|
+
start = sp + 1;
|
|
89
|
+
}
|
|
90
|
+
const slice = content.slice(start, start + budget);
|
|
91
|
+
return (start > 0 ? '…' : '') + slice + (start + budget < content.length ? '…' : '');
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Build the passage handed to the cross-encoder for one candidate.
|
|
95
|
+
*
|
|
96
|
+
* `mode`:
|
|
97
|
+
* - `'prefix'` (default) — legacy behaviour, the first `budget` chars.
|
|
98
|
+
* - `'query'` — the `budget`-char window densest in query terms.
|
|
99
|
+
*/
|
|
100
|
+
export function buildRerankPassage(concept, content, query, budget, mode, tags) {
|
|
101
|
+
const body = mode === 'query'
|
|
102
|
+
? densestWindow(content, query, budget)
|
|
103
|
+
: (content.length > budget ? content.slice(0, budget) : content);
|
|
104
|
+
// Topical tags, when enabled. The cross-encoder decides final order since
|
|
105
|
+
// phase 9b, and it cannot see tags at all — measured on the live store, 66.2%
|
|
106
|
+
// of topical tag terms never appear in the body, so that vocabulary is
|
|
107
|
+
// invisible to the stage that now decides ranking. Appended (not substituted)
|
|
108
|
+
// and length-capped so it cannot crowd out the content window.
|
|
109
|
+
const extra = rerankTagText(tags);
|
|
110
|
+
return extra ? `${concept}: ${body} ${extra}` : `${concept}: ${body}`;
|
|
111
|
+
}
|
|
112
|
+
/** Whether topical tags are appended to the rerank passage. Default OFF. */
|
|
113
|
+
export function rerankTagsEnabled() {
|
|
114
|
+
return process.env.AWM_RERANK_TAGS === '1';
|
|
115
|
+
}
|
|
116
|
+
/** Character budget for the appended tag text. */
|
|
117
|
+
export function rerankTagBudget() {
|
|
118
|
+
const v = Number(process.env.AWM_RERANK_TAGS_LEN ?? 80);
|
|
119
|
+
return Number.isFinite(v) && v > 0 ? v : 80;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Render topical tags as plain terms for the cross-encoder.
|
|
123
|
+
* Only `topic=` / `proj=` / `project=` carry query vocabulary; date/person/
|
|
124
|
+
* ticket tags are identifiers the body usually already contains, and adding
|
|
125
|
+
* them would spend the budget without adding reachable words.
|
|
126
|
+
*/
|
|
127
|
+
export function rerankTagText(tags) {
|
|
128
|
+
if (!rerankTagsEnabled() || !tags || tags.length === 0)
|
|
129
|
+
return '';
|
|
130
|
+
const words = new Set();
|
|
131
|
+
for (const t of tags) {
|
|
132
|
+
const m = /^(?:topic|proj|project)=(.+)$/i.exec(t);
|
|
133
|
+
if (!m)
|
|
134
|
+
continue;
|
|
135
|
+
for (const w of m[1].toLowerCase().split(/[-_\s]+/)) {
|
|
136
|
+
if (w.length > 2)
|
|
137
|
+
words.add(w);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (words.size === 0)
|
|
141
|
+
return '';
|
|
142
|
+
return `[${[...words].join(' ').slice(0, rerankTagBudget())}]`;
|
|
143
|
+
}
|
|
144
|
+
/** Character budget for a rerank passage. */
|
|
145
|
+
export function rerankTruncation() {
|
|
146
|
+
const v = Number(process.env.AWM_RERANK_TRUNC ?? 400);
|
|
147
|
+
return Number.isFinite(v) && v > 0 ? v : 400;
|
|
148
|
+
}
|
|
149
|
+
/** Passage selection mode. Default `prefix` preserves shipped behaviour. */
|
|
150
|
+
export function rerankWindowMode() {
|
|
151
|
+
return process.env.AWM_RERANK_WINDOW === 'query' ? 'query' : 'prefix';
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=rerank-window.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rerank-window.js","sourceRoot":"","sources":["../../src/core/rerank-window.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Second-stage reordering — "rerank the rerank".
|
|
3
|
+
*
|
|
4
|
+
* THE PROBLEM
|
|
5
|
+
* -----------
|
|
6
|
+
* Phase 7 blends the cross-encoder's judgement with the composite score:
|
|
7
|
+
*
|
|
8
|
+
* score = compositeWeight * composite + rerankWeight * rerankerScore
|
|
9
|
+
*
|
|
10
|
+
* with `rerankWeight` capped at 0.7, so `composite` always keeps at least 30%
|
|
11
|
+
* of the vote on final ordering. Composite carries decay, Hebbian and salience
|
|
12
|
+
* terms — useful for deciding WHICH candidates deserve consideration, but a
|
|
13
|
+
* poor judge of which one actually answers the question.
|
|
14
|
+
*
|
|
15
|
+
* Measured on LoCoMo (616 answerable probes, tests/rerank2-eval): the blend
|
|
16
|
+
* disagrees with the cross-encoder about which item deserves rank 1 on **38.6%**
|
|
17
|
+
* of queries. Where that disagreement is decidable — one of the two is the gold
|
|
18
|
+
* evidence — the **cross-encoder is right 77% of the time** (61 vs 18).
|
|
19
|
+
*
|
|
20
|
+
* THE FIX
|
|
21
|
+
* -------
|
|
22
|
+
* Re-sort only the final returned window by `rerankerScore` alone. Simulated
|
|
23
|
+
* gain: **+8.0pp success@1** (39.1% -> 47.1%), fixing 61 queries and breaking
|
|
24
|
+
* 12, with every category improving.
|
|
25
|
+
*
|
|
26
|
+
* WHY THIS IS SAFE
|
|
27
|
+
* ----------------
|
|
28
|
+
* Applied AFTER the channel-agreement gate, AFTER computeRecallConfidence, and
|
|
29
|
+
* AFTER the requireConfidence check. Those read rerankerScore maxima/margins and
|
|
30
|
+
* the score distribution. Reordering a window afterwards changes neither its
|
|
31
|
+
* membership nor any score, so **adversarial abstention is provably
|
|
32
|
+
* unaffected**. Retuning `rerankWeight` inside phase 7 would NOT have that
|
|
33
|
+
* property — it shifts `item.score`, hence which items clear `minScore`, which
|
|
34
|
+
* is exactly what cost adversarial 73.4->71.0 when the pool was last widened.
|
|
35
|
+
*
|
|
36
|
+
* Margin-guarded variants were simulated and are strictly worse: gating on a
|
|
37
|
+
* >0.15 reranker margin cuts breakage 12->1 but also cuts fixes 61->17.
|
|
38
|
+
*/
|
|
39
|
+
/** Minimal shape this needs — anything carrying a cross-encoder score. */
|
|
40
|
+
export interface RerankScored {
|
|
41
|
+
phaseScores: {
|
|
42
|
+
rerankerScore: number;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Re-sort the first `k` entries of `ranked` by descending `rerankerScore`,
|
|
47
|
+
* leaving the tail untouched. Returns a new array; never mutates the input.
|
|
48
|
+
*
|
|
49
|
+
* Returns `ranked` unchanged when reordering would be unsound:
|
|
50
|
+
* - `k <= 1`, or fewer than 2 entries to order
|
|
51
|
+
* - any entry in the window has `rerankerScore <= 0`, which means the
|
|
52
|
+
* cross-encoder did not score it (skipped, failed, or timed out). Sorting
|
|
53
|
+
* on a zero would scramble an otherwise sound composite ordering — the
|
|
54
|
+
* reranker is wrapped in try/catch and a 10s timeout upstream, so this is a
|
|
55
|
+
* real path, not a theoretical one.
|
|
56
|
+
*/
|
|
57
|
+
export declare function reorderByReranker<T extends RerankScored>(ranked: T[], k: number): T[];
|
|
58
|
+
/** Window size for the second stage. */
|
|
59
|
+
export declare function rerank2WindowSize(): number;
|
|
60
|
+
/** Whether the second stage is enabled. Default OFF pending the tracer verdict. */
|
|
61
|
+
export declare function rerank2Enabled(): boolean;
|
|
62
|
+
//# sourceMappingURL=rerank2.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rerank2.d.ts","sourceRoot":"","sources":["../../src/core/rerank2.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,0EAA0E;AAC1E,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;CACxC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,YAAY,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE,CAarF;AAED,wCAAwC;AACxC,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,mFAAmF;AACnF,wBAAgB,cAAc,IAAI,OAAO,CAExC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Second-stage reordering — "rerank the rerank".
|
|
5
|
+
*
|
|
6
|
+
* THE PROBLEM
|
|
7
|
+
* -----------
|
|
8
|
+
* Phase 7 blends the cross-encoder's judgement with the composite score:
|
|
9
|
+
*
|
|
10
|
+
* score = compositeWeight * composite + rerankWeight * rerankerScore
|
|
11
|
+
*
|
|
12
|
+
* with `rerankWeight` capped at 0.7, so `composite` always keeps at least 30%
|
|
13
|
+
* of the vote on final ordering. Composite carries decay, Hebbian and salience
|
|
14
|
+
* terms — useful for deciding WHICH candidates deserve consideration, but a
|
|
15
|
+
* poor judge of which one actually answers the question.
|
|
16
|
+
*
|
|
17
|
+
* Measured on LoCoMo (616 answerable probes, tests/rerank2-eval): the blend
|
|
18
|
+
* disagrees with the cross-encoder about which item deserves rank 1 on **38.6%**
|
|
19
|
+
* of queries. Where that disagreement is decidable — one of the two is the gold
|
|
20
|
+
* evidence — the **cross-encoder is right 77% of the time** (61 vs 18).
|
|
21
|
+
*
|
|
22
|
+
* THE FIX
|
|
23
|
+
* -------
|
|
24
|
+
* Re-sort only the final returned window by `rerankerScore` alone. Simulated
|
|
25
|
+
* gain: **+8.0pp success@1** (39.1% -> 47.1%), fixing 61 queries and breaking
|
|
26
|
+
* 12, with every category improving.
|
|
27
|
+
*
|
|
28
|
+
* WHY THIS IS SAFE
|
|
29
|
+
* ----------------
|
|
30
|
+
* Applied AFTER the channel-agreement gate, AFTER computeRecallConfidence, and
|
|
31
|
+
* AFTER the requireConfidence check. Those read rerankerScore maxima/margins and
|
|
32
|
+
* the score distribution. Reordering a window afterwards changes neither its
|
|
33
|
+
* membership nor any score, so **adversarial abstention is provably
|
|
34
|
+
* unaffected**. Retuning `rerankWeight` inside phase 7 would NOT have that
|
|
35
|
+
* property — it shifts `item.score`, hence which items clear `minScore`, which
|
|
36
|
+
* is exactly what cost adversarial 73.4->71.0 when the pool was last widened.
|
|
37
|
+
*
|
|
38
|
+
* Margin-guarded variants were simulated and are strictly worse: gating on a
|
|
39
|
+
* >0.15 reranker margin cuts breakage 12->1 but also cuts fixes 61->17.
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* Re-sort the first `k` entries of `ranked` by descending `rerankerScore`,
|
|
43
|
+
* leaving the tail untouched. Returns a new array; never mutates the input.
|
|
44
|
+
*
|
|
45
|
+
* Returns `ranked` unchanged when reordering would be unsound:
|
|
46
|
+
* - `k <= 1`, or fewer than 2 entries to order
|
|
47
|
+
* - any entry in the window has `rerankerScore <= 0`, which means the
|
|
48
|
+
* cross-encoder did not score it (skipped, failed, or timed out). Sorting
|
|
49
|
+
* on a zero would scramble an otherwise sound composite ordering — the
|
|
50
|
+
* reranker is wrapped in try/catch and a 10s timeout upstream, so this is a
|
|
51
|
+
* real path, not a theoretical one.
|
|
52
|
+
*/
|
|
53
|
+
export function reorderByReranker(ranked, k) {
|
|
54
|
+
if (k <= 1 || ranked.length <= 1)
|
|
55
|
+
return ranked;
|
|
56
|
+
const window = Math.min(k, ranked.length);
|
|
57
|
+
if (window <= 1)
|
|
58
|
+
return ranked;
|
|
59
|
+
const head = ranked.slice(0, window);
|
|
60
|
+
for (const r of head) {
|
|
61
|
+
if (!(r.phaseScores.rerankerScore > 0))
|
|
62
|
+
return ranked; // also catches NaN
|
|
63
|
+
}
|
|
64
|
+
const sorted = head.slice().sort((a, b) => b.phaseScores.rerankerScore - a.phaseScores.rerankerScore);
|
|
65
|
+
return sorted.concat(ranked.slice(window));
|
|
66
|
+
}
|
|
67
|
+
/** Window size for the second stage. */
|
|
68
|
+
export function rerank2WindowSize() {
|
|
69
|
+
return Number(process.env.AWM_RERANK2_K ?? 10);
|
|
70
|
+
}
|
|
71
|
+
/** Whether the second stage is enabled. Default OFF pending the tracer verdict. */
|
|
72
|
+
export function rerank2Enabled() {
|
|
73
|
+
return process.env.AWM_RERANK2 === '1';
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=rerank2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rerank2.js","sourceRoot":"","sources":["../../src/core/rerank2.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,sCAAsC;AACtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAOH;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,iBAAiB,CAAyB,MAAW,EAAE,CAAS;IAC9E,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,MAAM,CAAC;IAEhD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAC1C,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO,MAAM,CAAC;IAE/B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IACrC,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,aAAa,GAAG,CAAC,CAAC;YAAE,OAAO,MAAM,CAAC,CAAE,mBAAmB;IAC7E,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,aAAa,GAAG,CAAC,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;IACtG,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,iBAAiB;IAC/B,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC;AACjD,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,cAAc;IAC5B,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,KAAK,GAAG,CAAC;AACzC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reranker.d.ts","sourceRoot":"","sources":["../../src/core/reranker.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"reranker.d.ts","sourceRoot":"","sources":["../../src/core/reranker.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAyFH,iCAAiC;AACjC,wBAAsB,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,CAGhD;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAsB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAGvF"}
|
package/dist/core/reranker.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { AutoTokenizer, AutoModelForSequenceClassification, } from '@huggingface/transformers';
|
|
15
15
|
import { dispatchRerank, registerInProcessHandlers } from './ml-worker.js';
|
|
16
|
+
import { ensureModelCacheDir } from './model-cache.js';
|
|
16
17
|
const DEFAULT_MODEL = 'Xenova/ms-marco-MiniLM-L-6-v2';
|
|
17
18
|
const MODEL_ID = process.env.AWM_RERANKER_MODEL || DEFAULT_MODEL;
|
|
18
19
|
// --- In-process fallback ---
|
|
@@ -25,6 +26,7 @@ async function ensureLoaded() {
|
|
|
25
26
|
if (initPromise)
|
|
26
27
|
return initPromise;
|
|
27
28
|
initPromise = (async () => {
|
|
29
|
+
ensureModelCacheDir();
|
|
28
30
|
tokenizer = await AutoTokenizer.from_pretrained(MODEL_ID);
|
|
29
31
|
model = await AutoModelForSequenceClassification.from_pretrained(MODEL_ID, { dtype: 'fp32' });
|
|
30
32
|
console.error(`Re-ranker model loaded in-process: ${MODEL_ID}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reranker.js","sourceRoot":"","sources":["../../src/core/reranker.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,sCAAsC;AACtC;;;;;;;;;;GAUG;AAEH,OAAO,EACL,aAAa,EACb,kCAAkC,GAGnC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"reranker.js","sourceRoot":"","sources":["../../src/core/reranker.ts"],"names":[],"mappings":"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"}
|